在M1芯片的苹果电脑
中使用Xcode
编译模拟器时,可能会碰到如下报错:
原因是由于M1模拟器架构是arm64
架构,而Intel芯片
是x86_64
的架构,从而导致编译出现了问题。
这些报错,都是是由于项目中存在.a
或.framework
静态库导致的。以前,我们创建静态库时,会分别打包出一份针对真机(arm64)和模拟器的(x86_64),然后将这两份合并成一个包后引入项目中进行使用。在Intel
机型上,真机上使用arm64
指令,模拟器(x86_64)中使用x86_64
指令,所以不存在问题。但是在M1
机型上,模拟器是以arm64
运行的,显然再以x86_64
运行就会出现问题。
有同学可能会想到包中是有
arm64
指令(真机)的,拿给以arm64
运行的模拟器使用不就可以了吗? 实际上xcode底层并不是这样处理的,它真机就找真机的,模拟器就找模拟器的。
解决方案
常用方案
对于这类架构报错问题,网上的资料一般会告诉你两个解决方案:
-
以Rosetta模式运行Xcode。
-
修改
Build Settings
->Excluded Architectures
选项,添加Any iOS Simulator SDK选项,并设置值为arm64。图示如下: -
这两种方案都能解决编译问题,但是也都存在问题。
在iOS12及以后,不再支持iphone5及以下机型,而后续的机型都是arm64架构,所以这里不再对之前的armv6/armv7/armv7s/i386 等指令集进行说明。
Rosetta方案说明
以
Rosetta
模式运行是M1
机器上x86
软件无法运行的解决方案,它会将x86
指令转译成ARM
指令运行,这种转译显然是存在性能损耗的,损耗大概在20%~30%
,详情可参考文章:苹果换芯,成了开发者们的噩梦?https://m.huxiu.com/article/393879.html。 -
Excluded Architectures方案说明
修改
Excluded Architectures
选项也有它的问题。字面意思是排除架构的意思,我们设置在模拟器中排除arm64
就能解决模拟器无法编译arm64
的问题。这样的设置能生效会让人有点费解,我们知道,在intel机型上,模拟器本来就是以
x86
方式运行的,排除arm64
毫无影响。但是在M1
机型上,模拟器是以arm64
方式运行的,排除了arm64
反而能跑,这不是把我的智商摁在地上摩擦么?,但是苹果就是这样干的,当在M1
机型上,排除了模拟器的arm64
架构后,模拟器还是会以arm64
的方式运行,但是模拟器中的app是以x86
的方式运行的,对苹果的这个骚操作我们不得不服。图示如下: -
这种情况下,模拟器和应用会通过
XPC
进行通信,虽然理论上不会有问题,但通信时间会比较长,导致一些依赖计时器判断的逻辑会出问题,例如滑动手势,加速度的判断会出一些问题,导致模拟器里大部分情况下列表无法触发惯性滚动。其它问题
有时候在
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
在 Build Settings 中添加如图所示配置:
-
最优解
通过上述内容,我们知道了问题的由来,它是由于项目中存在
.a
或.framework
,它们提供的指令集不完整导致的。Apple对于这类问题,也提供了解决方案,请由我细细道来。以Xcode13为例,在我们创建静态库时,选择真机编译出来的包只包含
arm64
指令,选择模拟器编译出来的会同时包含arm64
和x86_64
指令。我看一些网上的教程,教别人将模拟器部分的arm64
移除,其实大可不必。因为要支持M1
机器正常跑模拟器,模拟器必须同时包含arm64
和x86_64
指令。2019年的WWDC
,apple
提供了一种新的框架封装格式XCFramework
。简单理解就是以前使用lipo
合并不同指令集的包,现在则使用新的指令合并成XCFramework
格式。打包成framework。
XCFramework
就是把两个不同指令集的framework
放入了同一个文件夹(.xcframework
),并生成了一个配置文件Info.plist
。这样生成的XCFramework
就可以完美的解决M1
机器无法编译模拟器的问题。 -
XCFramework
的创建指令也很简单:.a指令: xcodebuild -create-xcframework -library <path> [-headers <path>] [-library <path> [-headers <path>]...] -output <path> 样例: xcodebuild -create-xcframework -library youpath/xxx.a -headers youpath/xxx -library youpath/xxx.a -headers youpath/TestFramework -output youpath/xxx.xcframework .framework指令: xcodebuild -create-xcframework -framework <path> [-framework <path>...] -output <path> 样例: xcodebuild -create-xcframework -framework Release-iphoneos/xxx.framework -framework Release-iphonesimulator/xxx.framework -output xxx.xcframework
-
解决
M1
机型无法编译模拟器的关键就是针对模拟器的包要同时包含arm64
和x86_64
指令集。如果使用只支持x86_64
指令集的模拟器包,就算打包成XCFramework
也会依然存在这个问题。以现在的情况,很多第三方框架,并没有使用
XCFramework
,而项目中只要有一个框架没有支持模拟器的arm64
指令,那么在M1
机器上,模拟器只能以Rosetta
模式运行应用,对这一块的普遍支持估计要等M1普及以后了。