文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

iOS问题记录 - Building for iOS Simulator, but linking in dylib built for iOS, for architecture arm64

2023-08-23 18:35

关注

前言

最近用了一个第三方的Flutter插件,刚开始用项目运行到iOS模拟器就失败了。这个报错在老早以前出现的比较频繁,但是现在还出现属实不应该。

开发环境

问题描述

Flutter项目引入第三方插件后,在iOS模拟器运行项目报错:

Building for iOS Simulator, but linking in dylib built for iOS, file 'xxx' for architecture arm64

通过第三方插件ios目录下的xxx.podspec文件可知,这个插件依赖了一个Pod库(已编译的非开源库),插件的作用就是对Pod库进行二次封装。

问题分析

XCFramework出来之前,第三方库(已编译的非开源库)不区分真机和模拟,都是通过lipo命令将真机库和模拟器库合并为一个库,合并后的库中,arm64架构的属于真机,x86_64架构的属于模拟器。

Apple Silicon M1芯片出来前不区分真机和模拟器没什么问题,因为iOS模拟器确实都是x86_64架构的。M系列芯片出来后,Xcode 12支持了arm64架构的iOS模拟器。然后问题来了,在M系列芯片电脑上,Xcode会默认使用arm64架构的iOS模拟器,项目构建时会链接第三方库中arm64架构的二进制文件,又由于arm64架构的二进制文件是属于真机的,如果第三方库是动态库,那么就会出现问题描述中的错误,如果第三方库是静态库,那么出现的错误是这样的:

building for iOS Simulator, but linking in object file built for iOS, file 'xxx' for architecture arm64

这问题已经出来很久了,第三方库的作者早已经做了处理。要么去支持XCFramework,要么在Podspec文件中加上:

# Pod库的构建设置排除arm64架构模拟器s.pod_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' }# 主工程的构建设置排除arm64架构模拟器s.user_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' }

按道理来说,现在只要项目依赖的第三方库不是特别老的版本,基本不会遇到这种问题。不过我就是一个例外,同一个Flutter项目,连续在Intel芯片的Mac和M系列芯片的Mac遇到这个问题。

注意,以下分析基于Flutter插件,如果你是纯iOS开发,不了解也没有关系。因为Flutter插件只看iOS相关部分的话,就是一个Pod库,所以以下凡是涉及Flutter插件的都可以看作是Pod库。

1. Intel芯片的Mac上报错

首先对比已经引入的Flutter插件,没发现新引入的有什么问题。接着排查项目中涉及架构的构建设置,还真让我发现了一个问题,Pods工程中的Build Active Architecture Only设置有问题。正常情况下,多环境配置中的Dev环境(属于Debug环境)应该设为Yes,但是实际是No:

screenshot1

那么问题的原因是这个吗?当Build Active Architecture Only设为No时,Xcode构建项目不会局限于当前设备所支持的架构,通常用于Release环境,毕竟打的正式包一般不可能只在当前设备运行;设为Yes时,Xcode构建项目只会基于当前设备所支持的架构。

当前电脑是x86_64架构的,也就是说只支持x86_64架构的iOS模拟器,Build Active Architecture Only设为No意味着Xcode项目构建时将不限于x86_64架构,还会构建arm64架构的。这么一分析,好像确实如此。手动修改设置为Yes,项目重新运行到模拟器,一切正常!

手动修改肯定是不靠谱的,一开始遇到这个问题的时候,没想那么多,直接像这样在Podfile文件中通过Hook匹配构建配置名称将ONLY_ACTIVE_ARCH的值设为YES

post_install do |installer|  installer.pods_project.build_configurations.each do |config|    if config.name == 'Dev'      config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES'      break    end  endend

后来研究Flutter & iOS问题记录 - 多环境配置下Pod库的宏定义失效问题时,发现完全没必要用Hook,在Podfile文件中给多环境配置映射正确的配置类型才是正常的做法:

project 'app', {  'Dev' => :debug,  'Pre' => :release,  'Prod' => :release,}

这里有一个很重要的问题当时被我忽略了,在引入这个第三方Flutter插件之前已经引入了不少其他第三方Flutter插件,但是之前都没有报错,所以这个新引入的Flutter插件还是可能有问题的。如果后面没有再次遇到这个问题,我还真以为这个问题已经被彻底解决。

2. M系列芯片的Mac上报错

为了方便调试和分析,新建一个Flutter项目,同时参考报错的第三方Flutter插件也新建一个,就叫test_plugin。运行测试项目到iOS模拟器,问题成功复现,找到构建日志:

screenshot2

咦,怪不得报错,竟然会针对arm64架构进行编译链接。针对arm64架构编译不报错是因为这是源码编译,无所谓什么架构,链接报错是因为被链接的库(Flutter插件依赖的第三方库)不支持arm64架构的iOS模拟器。

找到test_plugin的构建设置:

screenshot3

模拟器竟然没有排除arm64架构,这是怎么回事?打开test_plugin.podspec文件发现了问题:

s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }

VALID_ARCHS设置已经过时失效,现在应该用EXCLUDED_ARCHS设置。关于这些构建设置的使用可以参考文档:TN3117: Resolving architecture build errors on Apple silicon

screenshot4

看来这报错的Flutter插件的创建时间有点久远了,从Flutter框架项目的提交记录看,在2020年Podspec文件模板已经开始改用EXCLUDED_ARCHS设置:

screenshot5

不过,改用新模板的设置能解决问题吗?看这也没排除arm64架构。

s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }

改为这个实测不行。在设置中的i386的后面加上arm64

s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386 arm64' }

再次运行,弹出了这个:

screenshot6

点击Build for Rosetta确实也能正常运行,如果想切换回原来的目标设备列表,按这个步骤[Xcode菜单栏] -> [Product] -> [Destination] -> [Destination Architectures] -> [Show Apple Silicon Destinations]操作。

虽然能运行,但是不想用Rosetta怎么办?很好解决,在主工程的构建设置中设置排除arm64架构的模拟器。有两种设置方式:

  1. 手动设置

screenshot7

  1. 通过Flutter插件的Podspec文件设置
s.user_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' }

注意,低版本的iOS模拟器只支持Rosetta,至于具体到多低的版本,推测是低于iOS 14的版本,因为iOS 14/Xcode 12/M1芯片都是2020年发布的。

可以通过file命令查看iOS模拟器支持的架构,例如查看Xcode 14.3内置的iOS模拟器:

file /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/Contents/MacOS/Simulator

执行输出:

/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/Contents/MacOS/Simulator: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64]/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/Contents/MacOS/Simulator (for architecture x86_64):Mach-O 64-bit executable x86_64/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/Contents/MacOS/Simulator (for architecture arm64):Mach-O 64-bit executable arm64

从输出内容可以看到, iOS 16.4模拟器同时支持x86_64和arm64架构。如果是 iOS 12.4模拟器,输出是这样的:

/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 12.4.simruntime/Contents/MacOS/iOS 12.4: Mach-O 64-bit bundle x86_64

iOS 12.4模拟器只支持x86_64架构,如果只显示Apple Silicon Destinations列表会出现找不到iOS 12.4模拟器的情况。建议按这个步骤[Xcode菜单栏] -> [Product] -> [Destination] -> [Destination Architectures] -> [Show Both]操作,显示全部iOS模拟器。

问题分析到这还没完,为什么其他还用着VALID_ARCHS设置的Flutter插件没报错呢?

3. 动态库引起的报错

对比多个Flutter插件的构建日志,我发现了报错的另一个原因,竟然是因为动态库。

Flutter插件报错时的构建日志:

screenshot2

test_plugin.podspec文件中加上这行代码指定为静态库:

s.static_framework = true

重新构建的日志:

screenshot8

一切正常!构建静态库时只编译不链接,避免了链接报错。等到构建APP时,由于主工程的构建设置中已经排除了arm64架构,所以只会基于x86_64架构构建,这时链接也不会报错。

虽然可以通过将动态库修改为静态库这种方式来解决问题,但这个问题本质还是架构相关的问题,所以还是围绕架构来解决。

解决方案

这里提供三个解决方法,请根据需要任选一个即可:

  1. iOS模拟器排除arm64架构(设置Pods项目的构建设置)

Podfile文件中加上:

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

这个方法只适用于解决因为使用过时VALID_ARCHS设置导致的报错。如果本身使用的是EXCLUDED_ARCHS设置,又没有加上$(inherited),会导致这个方法失效。例如Podspec文件这样设置:

s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }

会因为优先级问题,无法生效:

在这里插入图片描述

  1. iOS模拟器排除arm64架构(设置Target的构建设置)
post_install do |installer|  installer.pods_project.targets.each do |target|    target.build_configurations.each do |config|      excluded_archs = config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]']      excluded_archs = excluded_archs.nil? ? '' : excluded_archs      if !excluded_archs.include?('arm64')        excluded_archs = "#{excluded_archs} arm64"      end      config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = excluded_archs    end  endend
post_install do |installer|  installer.pods_project.targets.each do |target|    flutter_additional_ios_build_settings(target)    target.build_configurations.each do |config|      excluded_archs = config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]']      excluded_archs = excluded_archs.nil? ? '' : excluded_archs      if !excluded_archs.include?('arm64')        excluded_archs = "#{excluded_archs} arm64"      end      config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = excluded_archs    end  endend

根据EXCLUDED_ARCHS设置的值决定要不要追加排除arm64架构,如果值中已经有了arm64便不做改动。由于是设置Target的构建设置,优先级高,所以不会出现第一个方法中的问题。注意,尽量不要整个直接复制粘贴,请根据项目的实际情况修改。如有疑问,欢迎留言评论。

  1. 设置需要构建的架构(终极方法

Podfile文件中加上:

post_install do |installer|  installer.pods_project.build_configurations.each do |config|    config.build_settings['ARCHS[sdk=iphonesimulator*]'] = 'x86_64'  endend

这段代码的作用是设置iOS模拟器只构建x86_64架构,专治各种关于架构的问题,你值得拥有。

注意:修改Podfile文件后,需要重新执行pod install命令让改动生效。

2023/08/14更新:

如果以上三个解决方法都没能解决你的问题,请检查主工程的构建设置中是否设置了排除arm64架构的模拟器:

screenshot7

如果没有设置,手动设置即可。

补充内容1

你可能还会遇到以下问题:

出现这个问题的原因有两种。一是被链接的库(开源库)没有排除arm64架构,默认只编译了arm64架构,而第三方库排除了arm64架构,只编译x86_64架构;二是被链接的库(开源库)排除了arm64架构,只编译x86_64架构,但是第三方库没排除arm64架构。这叫什么?这叫互相错过,爱而不得。

根据构建日志,可以找到报错的文件xxx-Swift.h,内容是这样的:

#if 0#elif defined(__arm64__) && __arm64__...#else#error unsupported Swift architecture#endif

看到__arm64__,基本可以确定也是架构的问题。找到报错库的Podspec文件修改为排除arm64架构,重新构建不再报错,文件xxx-Swift.h的内容变为这样:

#if 0#elif defined(__x86_64__) && __x86_64__...#else#error unsupported Swift architecture#endif

以上问题都是关于架构的同一类问题,具体解决方法请看前面。

补充内容2

从前面的问题分析可知,现在的iOS模拟器同时支持x86_64和arm64架构,那么该如何判断当前运行的模拟器是哪个架构的呢?可以通过打印设备型号判断:

#import <sys/utsname.h>
struct utsname systemInfo;uname(&systemInfo);NSLog(@"machine: %@", @(systemInfo.machine));
var systemInfo = utsname()uname(&systemInfo)let machine = withUnsafePointer(to: &systemInfo.machine.0) { ptr in    return String(cString: ptr)}print("machine: \(machine)")

如果打印结果为machine: x86_64,说明当前运行的是x86_64架构的iOS模拟器;如果打印结果为machine: arm64,说明是arm64架构的iOS模拟器。

最后

如果这篇文章对你有所帮助,点赞👍加星🌟支持一下吧,谢谢~


本篇文章由@crasowas发布于CSDN。

来源地址:https://blog.csdn.net/crasowas/article/details/130278538

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯