今天我們來認識一位接觸 OpenHarmony 不到一年,便帶領團隊成功開發出一款“啟航 KP“智能開發套件的開發者——軟通動力資深項目經理許北林。 ...
主要是兩個錯誤,引起混淆。導致爬了挺久的坑。
1、 In xxxx/proj.ios_mac/xxxx.framework/xxxx(xxxx.a-arm64-master.o), building for iOS Simulator, but linking in object file built for iOS, for architecture arm64.
或者
ld: warning: ignoring file YoupPth/Build/Products/Debug-iphonesimulator/xxxx/xxxx.framework/xxxx,
building for iOS Simulator-x86_64 but attempting to link with file built for iOS Simulator-arm64
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_xxxx", referenced from:
objc-class-ref in xxxx.o
ld: symbol(s) not found for architecture x86_64
2、 In xxxx/proj.ios_mac/xxxx.xcodeproj The linked framework 'Pods_xxx_iOS.framework' is missing one or more architectures required by this target: x86_64.
看起來好像是差不多,都是由於 項目中的依賴庫 提供的指令集 不完整導致的。
第一個 是說 xxxx.framework 指令集為 iOS 真機 這個庫, 不能 鏈接 為 iOS 模擬器 的 目標文件。 實際上這個問題 應該是一個相容問題,只出現在 M1 晶元的 Mac 上。 因為 M1 晶元的 Mac 本身就是 arm64 架構,它的模擬器也是 arm64 架構的。
首先有一點要明確:
我們在 intel 機型上 創建一個靜態庫文件,編譯後會生成兩個版本。一個是模擬器版, x86_64 架構(如果M1 機型上創建 那麼是 arm64 架構),一個是真機版, arm64 架構。
如果 intel 機型上 生成的靜態庫 需要 給 intel 機型上的 其他工程引入,一般操作是通過 lipo 工具合併成一個 模擬器和真機 通用的 靜態庫,以方便瀏覽器或者真機調試。
鏈接真機的時候 會把真機的指令 鏈接進 目標文件,鏈接模擬器 就把模擬器的指令鏈接進 目標文件。
當鏈接的時候: 針對 一般第三方庫, 以前普遍提供的是 包含真機 arm64 指令集 和 模擬器 x86_64 指令集的 通用版本。
在 intel 機型上也就是你的Mac 打包機器為 intel 晶元, 為真機鏈接 arm64 架構 指令,為模擬器鏈接 x86_64 架構 指令沒有什麼問題。
但是 你的Mac 打包機器為 M1 晶元的時候, 為真機鏈接 arm64 架構指令,為模擬器 還是鏈接 x86_64 指令就會有問題。因為 此時 M1 機型 的模擬器架構是 arm64! 只能跑 arm64 指令。
這時候 Xcode 就會報以上類型的錯誤,而不會去為 模擬器 鏈接 該依賴庫 真機的 arm64 指令!!!
驗證很簡單,你在 M1 機型上編譯 真機的 靜態庫 鏈接到 模擬器 的可執行文件 依然不行。即使他們 都是在 arm64 架構下。
那麼如何解決這個問題? 有以下兩種方式。
1、使用 Rosetta 模式 運行Xcode, 重新編譯。
2005年, 蘋果從PowerPC 晶元切換到 因特爾,Rosetta 最初是為 PowerPC 應用轉換到 x86 上,能讓大多數 PowerPC 應用在 x86 Mac 上運行,但是會損失部分性能。
Rosetta 模式 會使 Xcode 能夠 在鏈接 x86靜態庫 前 將其轉換為 arm64 指令。
通過 Rosetta 模式運行 Xcode, Xcode 是以 x86架構的方式運行(正常是 Apple 也就是 arm64), 靜態庫是 x86 的也能正常鏈接跑。
在 M1 的 Mac 機型,模擬器 正常是以 arm64 運行的,模擬器 啟動 app 也是以 arm64 的方式運行。
而PC 上 Rosetta 模式啟動 Xcode 讓 App 鏈接 x86(也就是當前Xcode 運行模式)的庫,即使依賴庫既有 arm64 也有 x86 指令集。如果 依賴庫本身只有 arm64 指令集,那麼會報 未定義 符號,無法鏈接。
註意:Rosetta 的方式 雖然 足以支持大多數主流生產力應用程式,但無法相容那些需要與操作系統、硬體或圖形硬體進行直接交互的軟體。
2、編譯鏈接 app 工程的時候 排除 arm64 架構。 具體 在 Build Settings 里 設置 Excluded Architectures 選項,在 Debug 模式下 添加 arm64 。真機不會出現這個問題,所以 Release 不需要。
所以先 檢查工程以及子工程 編譯設置 排除 arm64 架構的編譯產物。 但是如果需要 編譯依賴庫文件 提供給第三方 使用 那麼你的生成產物又不能排除 arm64. 你可能就需要針對不同 的架構打不同的版本。下麵有更好的解決辦法。
檢查 Build Settings 裡面的 Excluded Architectures 添加 arm64。 clean 重編。
最終跑起來 會發現 排除了 arm64 指令集後 模擬器也是以 x86 的方式啟動 App 了。
查看 活動監視器 裡面 進程的 種類 就可以清晰的知道。
模擬器與 模擬器啟動的 App 運行指令集不同,通過 XPC 方式進行通信,理論上沒有啥問題,但是 XPC 通信 相對同步時間比較長, 對於 時間敏感的邏輯可能會出問題。
比如 滑動手勢 或者 列表的慣性滾動。
對於通過 cocoapods 維護依賴庫的方式來說, 如果手動更改後,重新 pod update 會導致又要重新修改,所以excluded Architectures 需要寫進 Podfile
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = "arm64"
end
end
end
為瞭解決 M1 晶元機型 封裝庫文件的問題, 2019年 的 WWDC 大會 Apple 提供了 XCFrameworks 的方式,這就可以理解成 新的 xcodebuild 命令替換以前 lipo 命令,將不同指令集 的庫文件合併成 通用二進位文件。
只不過 XCFrameworks 還可以包含其他第三方的庫。 這裡就不展開說 XCFrameworks 了。
第二個 是說 鏈接 Pods_xxxx_iOS.framework 這個framework 中 缺失了 x86_64 架構 的某個庫。
所以檢查 編譯 生成 Pods_xxxx_iOS.framework 的 所有依賴庫的工程 或者 prebuilt 的庫,是否存在 x86_64 架構。
1、針對子工程,如果 Xcode 13, 檢查工程的 User-Defined 裡面的 VALID_ARCHS 值 是否存在 x86_64,沒有則需要添加。 如果是 Xcode 12 則在 Architectures 裡面檢查 是否存在 x86_64。其他架構也是同樣
2、針對 預編譯庫, 可以使用 lipo 工具查看包含一些什麼 指令集。是否缺失 x86_64。 例如:lipo -i xxx.a
主要是 Xcode12 預設不再對 x86_64 架構 進行支持了,打通用包的時候 需要手動添加。
lipo 工具 可以對 庫文件的架構進行修改
nm 工具 用來現實一個 程式包的符號表
strip 用來刪除一個 程式包里的符號表
ar 工具 可以獲取 庫文件 鏈接前的 .o 文件
libtool -static -o xxx.a *.o 又可以合併 *.o 文件 為 xxx.a。 或者 直接用 ld。
參考:
https://juejin.cn/post/7037037120158269448