阅读 932

M1设备的Xcode编译问题深究(m1芯片 xcode)

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

ld: in youpath/Pods/UMCommon/UMCommon_7.3.5/UMCommon.framework/UMCommon(UMComBaseEvent.o),  building for iOS Simulator, but linking in object file built for iOS,  file 'youpath/Pods/UMCommon/UMCommon_7.3.5/UMCommon.framework/UMCommon' for architecture arm64 复制代码

ld: warning: ignoring file YoupPth/Build/Products/Debug-iphonesimulator/FMDB/FMDB.framework/FMDB,  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_$_FMDatabaseQueue", referenced from:       objc-class-ref in SqflitePlugin.o ld: symbol(s) not found for architecture x86_64 复制代码

这些报错,都是是由于项目中存在.a.framework静态库导致的。以前,我们创建静态库时,会分别打包出一份针对真机(arm64)和模拟器的(x86_64),然后将这两份合并成一个包后引入项目中进行使用。在Intel机型上,真机上使用arm64指令,模拟器(x86_64)中使用x86_64指令,所以不存在问题。但是在M1机型上,模拟器是以arm64运行的,显然再以x86_64运行就会出现问题。

有同学可能会想到包中是有arm64指令(真机)的,拿给以arm64运行的模拟器使用不就可以了吗? 实际上xcode底层并不是这样处理的,它真机就找真机的,模拟器就找模拟器的。

解决方案

常用方案

对于这类架构报错问题,网上的资料一般会告诉你两个解决方案:

  1. 以Rosetta模式运行Xcode。

  2. 修改Build Settings -> Excluded Architectures选项,添加Any iOS Simulator SDK选项,并设置值为arm64。图示如下:

这两种方案都能解决编译问题,但是也都存在问题。

在iOS12及以后,不再支持iphone5及以下机型,而后续的机型都是arm64架构,所以这里不再对之前的armv6/armv7/armv7s/i386 等指令集进行说明。

Rosetta方案说明

Rosetta模式运行是M1机器上x86软件无法运行的解决方案,它会将x86指令转译成ARM指令运行,这种转译显然是存在性能损耗的,损耗大概在20%~30%,详情可参考文章:苹果换芯,成了开发者们的噩梦?,不到万不得已,不推荐使用这种方案。

Excluded Architectures方案说明

修改Excluded Architectures选项也有它的问题。字面意思是排除架构的意思,我们设置在模拟器中排除arm64就能解决模拟器无法编译arm64的问题。

这样的设置能生效会让人有点费解,我们知道,在intel机型上,模拟器本来就是以x86方式运行的,排除arm64毫无影响。但是在M1机型上,模拟器是以arm64方式运行的,排除了arm64反而能跑,这不是把我的智商摁在地上摩擦么?,但是苹果就是这样干的,当在M1机型上,排除了模拟器的arm64架构后,模拟器还是会以arm64的方式运行,但是模拟器中的app是以x86的方式运行的,对苹果的这个骚操作我们不得不服。图示如下:

这种情况下,模拟器和应用会通过XPC进行通信,虽然理论上不会有问题,但通信时间会比较长,导致一些依赖计时器判断的逻辑会出问题,例如滑动手势,加速度的判断会出一些问题,导致模拟器里大部分情况下列表无法触发惯性滚动。 - by kem

其它问题

有时候在Excluded Architectures选项中排除了模拟器的arm64指令,依然无法编译通过,那么一般是项目设置和cocoapods的设置不一致导致,设置为一致后一般可以解决问题。可以通过在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 复制代码

最优解

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

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

2019年的WWDCapple提供了一种新的框架封装格式XCFramework。简单理解就是以前使用lipo合并不同指令集的包,现在则使用新的指令合并成XCFramework格式。

打包成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的创建指令也很简单:

# -- 针对.a -- # 指令: xcodebuild -create-xcframework -library <path> [-headers <path>] [-library <path> [-headers <path>]...] -output <path> # 样例: xcodebuild -create-xcframework -library youpath/TestFramework.a -headers youpath/TestFramework -library youpath/TestFramework.a -headers youpath/TestFramework -output youpath/TestFramework.xcframework  # -- 针对.framework -- # 指令: xcodebuild -create-xcframework -framework <path> [-framework <path>...] -output <path> # 样例: xcodebuild -create-xcframework -framework Release-iphoneos/TestFramework.framework -framework Release-iphonesimulator/TestFramework.framework -output TestFramework.xcframework 复制代码

解决M1机型无法编译模拟器的关键就是针对模拟器的包要同时包含arm64x86_64指令集。如果使用只支持x86_64指令集的模拟器包,就算打包成XCFramework也会依然存在这个问题。

后记

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


作者:星的天空
链接:https://juejin.cn/post/7037037120158269448

 伪原创工具 SEO网站优化  https://www.237it.com/ 


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐