前言
原生的应用程序比转换的应用程序运行效率更高,因为编译器能够针对目标架构来优化代码。如果一个应用程序只支持 x86_64 架构,那必须在 Apple 芯片上的 Rosetta 转换下运行。通用二进制文件本身就可以在 Apple 芯片和基于 Intel 的 Mac 机上运行,因为它包含了两种架构的可执行代码。
以下列表包含了最通用的可执行文件类型,它们可以转换为通用二进制文件。ps: 该列表并不详尽,但是可以将其用作评估项目的入口。
•Apps•App extensions•Plug-ins•Custom frameworks•Static libraries•Dynamic libraries•Build tools•Command-line tools•Daemons and launch agents•DriverKit extensions•Kernel extensions
下载安装最新版本的 Xcode12
早期版本的 Xcode 不包含构建和测试 macOS 代码通用版本所需的支持。
更新 Xcode 工程架构列表
Xcode 12 及更高版本会自动将 arm64 架构添加到所有 macOS 二进制文件(包括应用程序和库)的标准架构列表中。在调试和测试过程中,在默认情况下,Xcode 仅针对当前系统架构构建版本,但是,它会为代码的 Release 版本自动构建通用二进制文件。
如果您在 Xcode 工程中自定义了 Architectures 构建设置,请删除您的自定义项并改用 Standard Architectures 设置。
更新自定义 Makefile 结构列表
如果使用自定义脚本或 makefile 构建项目,则将 arm64 架构添加到适当的环境变量中。Xcode 使用 ARCHS 环境变量来定义当前的构建架构。其他构建系统可能使用不同的环境变量,但目的相似。将变量添加到适当的环境变量后,编译代码并验证编译器是否创建了代码的 arm64 版本。要为项目创建通用二进制文件,请使用 lipo 工具将生成的可执行文件合并为单个可执行二进制文件。
对于在 Xcode 之外创建的 makefile,请使用 -target 选项将适当的架构值传递给编译器。以下示例显示了一个 makefile 一次编译一个源文件两次,每种架构一次。然后,通过将生成的可执行文件与 lipo 工具合并在一起,创建通用二进制文件。
代码语言:javascript复制x86_app: main.c
$(CC) main.c -o x86_app -target x86_64-apple-macos10.12
arm_app: main.c
$(CC) main.c -o arm_app -target arm64-apple-macos11
universal_app: x86_app arm_app
lipo -create -output universal_app x86_app arm_app
使用宏封装特定平台的代码
在为特定平台或处理器类型编写代码时,请使用适当的条件编译语句隔离该代码。对于基于 C 的代码,系统定义了一组宏供您在 /usr/include/TargetConditionals.h 中使用。Swift语言还支持使用条件编译块进行条件编译。如果跨多个平台共享代码,则还可以在条件编译语句中使用特定于编译器的宏,例如 arm64 或 aarch64 。
为了区分特定类型处理器的代码,请添加针对适当架构的条件编译语句。通用 macOS 应用程序支持 arm64 和 x86_64 体系结构,以下示例显示了如何为这些架构编写条件代码:
代码语言:javascript复制#if arch(arm64)
// Code meant for the arm64 architecture here.
#elseif arch(x86_64)
// Code meant for the x86_64 architecture here.
#endif
如果在 iOS 和 macOS 应用之间共享代码,不要以为用于 arm64 架构的代码仅在 iOS 设备上运行,该代码也可以在 基于 Apple 芯片上的 macOS 应用中运行,要区分 macOS 或 iOS 平台,请使用以下示例中显示的条件编译语句。
代码语言:javascript复制#if os(macOS)
// Put CPU-independent macOS code here.
#if arch(arm64)
// Put 64-bit arm64 Mac code here.
#elseif arch(x86_64)
// Put 64-bit x86_64 Mac code here.
#endif
#elseif targetEnvironment(macCatalyst)
// Put Mac Catalyst-specific code here.
#elseif os(iOS)
// Put iOS-specific code here.
#endif
有关条件编译宏的完整列表,请参见 /usr/include/TargetConditionals.h 头文件。
编译你的 Target
当你在可调式版本下编译代码时,默认情况下 Xcode 只会针对当前的架构编译。您可以通过更改项目的 Build Active Architecture Only 选项来在任意的 Mac 机上创建带有调试符号的通用二进制文件。尽管您可以在基于 Intel 的 Mac 计算机上创建此二进制文件,但是无法在 arm64 架构上运行或者调试,只有具有 Apple 芯片的 Mac 才能运行和调试。
判断您的二进制文件是否通用
对用户而言,通用二进制文件看起来与为单个架构构建的二进制文件没有什么不同。当您构建通用二进制文件时,Xcode 会两次编译您的源文件,每种架构一次,Link 每种架构的二进制文件后,Xcode 使用 lipo 工具将特定架构的二进制文件合并到单个可执行文件中。如果自己编译源文件,则必须在构建脚本中调用 lipo,将特定架构的二进制文件合并为单个通用二进制文件。
以下示例显示了如何使用 lipo 在 macOS 中查看 Mail 应用程序的架构:
代码语言:javascript复制% lipo -archs /System/Applications/Mail.app/Contents/MacOS/Mail
x86_64 arm64
指定应用程序的启动行为
对于通用二进制文件,系统倾向于执行当前平台原生的架构,在基于 Intel 的 Mac 电脑上,系统始终执行 x86_64 架构。在 Apple 芯片上,系统倾向于在 arm64 架构上执行。用户可以通过在 Finder 的 "显示简介" 窗口中启用相应的选项,来强制系统在 Rosetta 转换下运行该应用程序。
如果您不希望用户在 Rosetta 转换下运行您的应用程序,请将 LSRequiresNativeExecution 键添加到应用程序的 Info.plist 文件中。当其设置为 YES 时,系统会阻止您的应用在转换后运行。另外,系统还会从您应用的 "显示简介" 窗口中删除 Rosetta 转换选项。在确认您的应用程序可以在 Apple 芯片 和基于 Intel 的 Mac 电脑上正常运行之前,请不要包含此键值对。
如果您想设置架构的优先级,而又不阻止用户在转换过程中运行您的应用,请将 LSArchitecturePriority 键添加到您应用的 Info.plist 文件中。该键的值是字符串的有序数组,它们定义了选择架构的优先级顺序。
本篇文章到此就结束了,如果想要更进一步的了解,请移步:
https://developer.apple.com/documentation/xcode/building_a_universal_macos_binary