M1 设备Xcode编译问题 Undefined symbols for architecture x86_64

在Apple发布M1芯片之前,一直使用Intel的芯片,没有出现什么问题。发布M1芯片后,由于两者架构的不同(M1是arm64架构,Intel是x86_64的架构),导致很多软件运行出现了问题。我们在M1机型中使用Xcode编译模拟器时,可能会碰到如下报错。

building for iOS Simulator-x86_64 but attempting to link with file built for iOS Simulator-arm64

解决方案
1.以Rosetta模式运行Xcode。
M1 设备Xcode编译问题 Undefined symbols for architecture x86_64_第1张图片
2.修改Build Settings -> Excluded Architectures选项,添加Any iOS Simulator SDK选项,并设置值为arm64。图示如下:
M1 设备Xcode编译问题 Undefined symbols for architecture x86_64_第2张图片
这两种方案都能解决编译问题,但是也都存在问题。

问题描述

Rosetta方案说明
以Rosetta模式运行是M1机器上x86软件无法运行的解决方案,它会将x86指令转译成ARM指令运行,这种转译显然是存在性能损耗的,损耗大概在20%~30%,但是,可以肯定的是,基于 ARM 的苹果芯片 Mac 性能更强,甚至足以抵消引入 Rosetta 2 带来的速度劣势,最终实现与一两年前大部分英特尔芯片版 Mac 相同的性能水平。

Excluded Architectures方案说明
修改Excluded Architectures选项也有它的问题。字面意思是排除架构的意思,我们设置在模拟器中排除arm64就能解决模拟器无法编译arm64的问题。
这样的设置能生效会让人有点费解,我们知道,在intel机型上,模拟器本来就是以x86方式运行的,排除arm64毫无影响。
但是在M1机型上,模拟器是以arm64方式运行的,排除了arm64反而能跑,但是苹果就是这样干的,当在M1机型上,排除了模拟器的arm64架构后,模拟器还是会以arm64的方式运行,但是模拟器中的app是以x86的方式运行的。

其它问题
有时候在Excluded Architectures选项中排除了模拟器的arm64指令,依然无法编译通过,那么一般是项目设置和cocoapods的设置不一致导致,设置为一致后一般可以解决问题。可以通过在Podfile中添加如下内容来解决:

post_install do |installer|
  installer.pods_project.build_configurations.each do |config|
    config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
  end
end

最优解

通过上述内容,我们知道了问题的由来,它是由于项目中存在.a或.framework,它们提供的指令集不完整导致的。Apple对于这类问题,也提供了解决方案,请由我细细道来。

以Xcode13为例,在我们创建静态库时,选择真机编译出来的包只包含arm64指令,选择模拟器编译出来的会同时包含arm64和x86_64指令。我看一些网上的教程,教别人将模拟器部分的arm64移除,其实大可不必。因为要支持M1机器正常跑模拟器,模拟器必须同时包含arm64和x86_64指令。

2019年的WWDC,apple提供了一种新的框架封装格式XCFramework。简单理解就是以前使用lipo合并不同指令集的包,现在则使用新的指令合并成XCFramework格式。我们将其与Framework的结构比对一下:
Framework:

$ tree Release-iphoneos/TestFramework.framework
Release-iphoneos/TestFramework.framework
├── Headers
│   ├── TestFramework.h
│   └── TestManager.h
├── Info.plist
├── Modules
│   └── module.modulemap
└── TestFramework

$ tree Release-iphonesimulator/TestFramework.framework
Release-iphonesimulator/TestFramework.framework
├── Headers
│   ├── TestFramework.h
│   └── TestManager.h
├── Info.plist
├── Modules
│   └── module.modulemap
├── TestFramework
└── _CodeSignature
    ├── CodeDirectory
    ├── CodeRequirements
    ├── CodeRequirements-1
    ├── CodeResources
    └── CodeSignature

XCFramework:

$ tree TestFramework.xcframework
TestFramework.xcframework
├── Info.plist
├── ios-arm64
│   └── TestFramework.framework
│       ├── Headers
│       │   ├── TestFramework.h
│       │   └── TestManager.h
│       ├── Info.plist
│       ├── Modules
│       │   └── module.modulemap
│       └── TestFramework
└── ios-arm64_x86_64-simulator
    └── TestFramework.framework
        ├── Headers
        │   ├── TestFramework.h
        │   └── TestManager.h
        ├── Info.plist
        ├── Modules
        │   └── module.modulemap
        ├── TestFramework
        └── _CodeSignature
            ├── CodeDirectory
            ├── CodeRequirements
            ├── CodeRequirements-1
            ├── CodeResources
            └── CodeSignature

从上述可以看出,XCFramework就是把两个不同指令集的framework放入了同一个文件夹(.xcframework),并生成了一个配置文件Info.plist。这样生成的XCFramework就可以完美的解决M1机器无法编译模拟器的问题。

小结:
以现在的情况,很多第三方框架,并没有使用XCFramework,而项目中只要有一个框架没有支持模拟器的arm64指令,那么在M1机器上,模拟器只能以Rosetta模式运行应用,对这一块的普遍支持估计要等M1普及以后了。

参考资料
苹果换芯,成了开发者们的噩梦?

你可能感兴趣的:(xcode,ios)