[转载] 告别构建错误, iOS 开发架构难题全面解析, 避免 CPU 架构陷阱
前言
如果你经常开发 iOS 中的第三方框架,那么你可能会遇到以下错误:
1 |
"Could not find module *** for target 'x86_64-apple-ios-simulator'." |
或者:
1 |
"building for iOS Simulator, but linking in dylib built for iOS, file, '.../Frameworks/xxx.framework/xxx' for architecture arm64." |
要解决这个问题,我们需要了解 CPU 架构和 Xcode 构建设置的一些知识,今天我们就来聊聊这个。
理解 CPU 架构
每个 CPU 都有一组可以执行的指令。这些指令主要分为两种类型:
CISC(复杂指令集计算)
- 复杂且强大的指令。
- 每条指令执行多个任务。
- 例如:x86 处理器(由 Intel 和 AMD 使用)。
RISC(精简指令集计算)
- 简单且快速的指令。
- 每条指令执行一个任务。
- 例如:ARM 处理器(由 Qualcomm、MediaTek 和苹果的 M1 芯片使用)。
什么是 32 位和 64 位
32 位:一次可以处理 32 位数据。
64 位:一次可以处理 64 位数据,允许更多的计算能力和内存使用。
常见架构
x86:Intel 和 AMD 使用的 32 位架构。
x86_64:x86 的 64 位版本,更强大,能处理更多数据。
ARM:Qualcomm 和 MediaTek 使用的 32 位架构。
ARM64:ARM 的 64 位版本,更强大,苹果的 M1 芯片使用。
主要的制造商
Intel 和 AMD:制造 x86 和 x86_64 处理器。
Qualcomm 和 MediaTek:制造 ARM 和 ARM64 处理器。
Apple:在 Apple Silicon 系列(M1、M1、M2、M3、M4 等)Mac 中使用
ARM64 处理器。
向 M 系列过渡与 Rosetta 的作用
M 系列处理器的引入,始于 M1,标志着苹果及其生态系统的重大转变。由于 M 系列基于 ARM,现有为 x86 构建的软件无法在这些新芯片上原生运行。为弥补这一差距,苹果推出了 Rosetta,它是一种兼容层,主要作用是允许 x86 软件在 M 系列处理器上运行。
M1 MacBooks 推出后,Xcode 最初就是使用 Rosetta 支持 x86 应用程序。
虽然这让开发者可以继续无缝工作,但 Rosetta 只是 Apple Silicon 过渡期的临时解决方案。随着 Xcode 12 的推出,苹果使 Xcode 能够在 ARM 上原生运行,全力支持 M 系列 MacBooks,而不再依赖于 Rosetta。
iOS 14 之前的模拟器仅限于 x86,并通过 Rosetta 在 M 系列 Mac 上运行。自 iOS 14 起,模拟器更新支持 ARM 和 x86,这意味着虽然模拟器可以在 M 系列 Mac 上原生运行,但未为 ARM 优化的应用程序仍会通过 Rosetta 运行。这种双重架构支持确保了过渡期间的兼容性和性能。
要检查你的应用程序正在使用哪种架构,你可以使用活动监视器。在活动监视器中,有一个名为”Kind”的列,显示应用程序是运行在 Intel(x86)还是 Apple(ARM)架构下。这一功能仅在 M 系列 Mac 上可用。
Apple 物理设备架构
- arm64:也称为 AArch64,现代 64 位 iOS 设备(iPhone 5S及更新机型),包括 A7、A8、A9、A10 和 A11 芯片的设备。
- arm64e:较新的 64 位 iOS 设备,带有 A12仿生芯片及更新版本(例如,iPhone XS、XR、11、12、13 等)。
- armv7:较旧的 32 位 iOS 设备(iPhone 3GS、4、4S)。
- armv7s:略新的 32 位设备(iPhone 5、5C)。
Apple 模拟器架构
- x86_64:基于 Intel 的 Mac 上的模拟器。
- i386:用于较旧 iOS 版本的 32 位模拟器(主要是遗留支持)。
- arm64:Apple Silicon(M1、M2)Mac 上的模拟器。
在了解了 CPU 架构基础知识、列出设备和模拟器架构,并讨论了 M
系列的历史和 Rosetta
的作用后,为解决之前提到的错误,我们需要知道如何找出我们集成的框架所支持的架构。
此外,我们需要调整构建设置,如 EXCLUDED_ARCHS 和
ONLY_ACTIVE_ARCH。接下来聊聊这些。
确定支持的架构
要确定 .xcframework 支持的架构,我们可以在终端中使用 lipo -info 命令。通过检查 .xcframework 中的目录,我们可以识别出支持哪些架构。以下是使用 lipo -info ~/Downloads/Bugly.framework/Bugly
查看 Bugly 2.6.0 版本的示例:
根据命令输出可以看出 Bugly 2.6.0 版本支持 armv7、i386、x86_64 和 arm64 架构。
理解 EXCLUDED_ARCHS 和 ONLY_ACTIVE_ARCH
EXCLUDED_ARCHS
- 定义:Xcode 中的一个构建设置,用于指定在构建目标时要排除的架构。
- 用途:排除某些架构以避免兼容性问题或不必要的构建。
- 示例:EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64 在为 iOS 模拟器构建时排除 arm64 架构,以确保兼容基于 Intel 的 Mac 上的 x86_64 模拟器。
ONLY_ACTIVE_ARCH
- 定义:一个构建设置,决定 Xcode 是仅构建活动架构还是所有指定架构。
- 用途:通过仅构建活动架构来加快开发过程中的构建速度。
- 示例:ONLY_ACTIVE_ARCH = YES 仅构建活动架构,在开发过程中减少构建时间。通常在 Debug 配置中设置为 YES,在 Release 配置中设置为 NO,以确保最终构建支持所有所需架构。
说白了一个是黑名单,一个是白名单。
之前提到的错误
错误消息”Could not find module *** for target ‘x86_64-apple-ios-simulator’.”通常表示我们尝试使用的框架或模块不可用于我们目标的架构,它需要原生运行而不是使用 Rosetta,但我们试图导入仅为 x86_64 构建的框架或可测试应用:
解决方法
我们可以实施一种变通方法来让测试目标运行,但这取决于使用的机器:
Apple Silicon M 系列:在 EXCLUDED_ARCHS 中排除 arm64。
基于 Intel 的:在 Debug 模式下使用”仅构建活动架构”,这将允许项目成功运行。
更好的解决方案
如果你可以控制框架的构建,建议打包的时候支持缺失的架构,特别是 arm64 模拟架构。
在分发的时候,优先使用 ios-arm64_i386_x86_64-simulator 或 ios-arm64_x86_64-simulator,更旧的模拟器一般就不用支持了。
如果你无法控制框架的构建,也无法联系开发者,你需要使用 EXCLUDED_ARCHS 排除缺失的架构。然而,这种方法可能限制你只能在物理设备上运行,特别是对于 Apple M 系列。此外,它要求你在使用此框架的所有依赖项中排除缺失的架构。例如,如果你使用的核心框架依赖于缺少架构的库,则核心框架和导入核心的项目都必须排除相同的架构。因此,在解决此类错误时要谨慎和耐心。