Xcode 26 真机运行崩溃 EXC_BAD_ACCESS map_images_nolock 完美解决方案

Xcode 26 真机运行崩溃 EXC_BAD_ACCESS map_images_nolock 完美解决方案

问题描述

升级 Xcode 26(26.0 / 26.2)后,项目在真机调试和打包时,App 启动阶段直接崩溃,卡在启动页无法进入主界面。模拟器无法复现(Flutter 混编项目模拟器不支持 release 构建)。

Xcode 崩溃信息:

复制代码
Thread 1: EXC_BAD_ACCESS (code=2, address=0x10525a7c0)

崩溃调用栈:

复制代码
libobjc.A.dylib`map_images_nolock

崩溃精确位置在 map_images_nolock 函数内部的 "fix up @protocol references" 阶段,汇编指令为:

asm 复制代码
-> str x8, [x27, x25, lsl #3]   ; 尝试写入 __objc_protorefs 段

控制台同时输出以下警告:

复制代码
objc[1218]: Class Statistics is implemented in both 
  .../Frameworks/ffmpegkit.framework/ffmpegkit and 
  .../Frameworks/ChinaArtGallery_protoc.framework/ChinaArtGallery_protoc. 
  One of the two will be used. Which one is undefined.

objc[1218]: Class Keychain is implemented in both 
  .../Frameworks/AliyunPlayer.framework/AliyunPlayer and 
  .../中华珍宝馆.debug.dylib. 
  One of the two will be used. Which one is undefined.

环境信息

项目 版本
Xcode 26.2 (Build 17C52)
目标设备 iPhone 真机,iOS 18
项目类型 ObjC + Flutter 混编
依赖管理 CocoaPods (use_frameworks!)
部署目标 iOS 13.0

根因分析

1. 崩溃本质:写入只读内存

EXC_BAD_ACCESS (code=2) 的 code=2 对应 KERN_PROTECTION_FAILURE,意味着目标内存地址存在,但当前是只读的。

ObjC 运行时在 map_images_nolock 中加载所有 image(framework、dylib、可执行文件)时,会执行 protocol references fixup (协议引用修复),需要将协议引用指针写入 __objc_protorefs section。

2. Xcode 26 链接器行为变化

这是问题的核心。

Xcode 26 的链接器(ld)默认将 __objc_protorefs 等 ObjC 元数据放入 __DATA_CONST segment。__DATA_CONST 在 dyld 完成自身 fixup 后会被标记为只读。

而 iOS 18 设备上的 ObjC 运行时(libobjc.A.dylib)在执行 protocol fixup 时,并不会主动将 __DATA_CONST 改为可写 ,它假设 __objc_protorefs 在可写的 __DATA segment 中。

时序如下:

复制代码
1. dyld 加载所有 image
2. dyld 将 __DATA_CONST 设为可写,执行 rebase/bind fixup
3. dyld 完成 fixup,将 __DATA_CONST 标记为只读 ← 关键步骤
4. dyld 触发 map_images 通知
5. ObjC 运行时 map_images_nolock 执行 protocol fixup
6. 尝试写入 __DATA_CONST 中的 __objc_protorefs → 💥 CRASH

在 Xcode 16 及更早版本中 ,链接器将 __objc_protorefs 放在 __DATA(始终可写),所以不会崩溃。

3. 为什么只有真机崩溃

模拟器使用 x86_64/arm64 模拟器运行时(来自 Xcode SDK),与真机的 arm64 运行时行为不完全一致。真机上 iOS 18 的 libobjc 对 __DATA_CONST 的内存保护更严格。

4. 重复类加剧问题

虽然重复类(Statistics、Keychain)本身只是警告,但它们会导致运行时需要处理更多的 protocol fixup,增大了触发崩溃的概率。

解决方案

核心修复:添加 -Wl,-no_data_const 链接器标志

该标志告诉 Xcode 26 的链接器不使用 __DATA_CONST segment ,将所有数据(包括 __objc_protorefs)放入可写的 __DATA segment。

关键点:必须同时对主工程和所有 CocoaPods targets 生效。

步骤 1:修改主工程 OTHER_LDFLAGS

在 Xcode 中选择主 target → Build Settings → Other Linker Flags,添加:

复制代码
-Wl,-no_data_const

或者直接编辑 project.pbxproj,在 Debug 和 Release 的 OTHER_LDFLAGS 中添加:

复制代码
OTHER_LDFLAGS = (
    "$(inherited)",
    "-ObjC",
    "-Wl,-no_data_const",
);
步骤 2:修改 Podfile,给所有 Pod targets 添加该标志

由于项目使用 use_frameworks!(动态框架),每个 Pod 都是独立编译链接的 framework。只改主工程不够,必须给每个 Pod target 也加上这个标志。

在 Podfile 的 post_install 中添加:

ruby 复制代码
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      # Xcode 26: 强制 __objc_protorefs 放入可写的 __DATA 段,避免 map_images_nolock 崩溃
      other_ldflags = config.build_settings['OTHER_LDFLAGS'] || ['$(inherited)']
      other_ldflags = [other_ldflags] if other_ldflags.is_a?(String)
      unless other_ldflags.include?('-Wl,-no_data_const')
        other_ldflags << '-Wl,-no_data_const'
      end
      config.build_settings['OTHER_LDFLAGS'] = other_ldflags
    end
  end
end
步骤 3:重新安装 Pod 并 Clean Build
bash 复制代码
pod install

然后在 Xcode 中 Product → Clean Build Folder (Shift+Cmd+K),再真机运行。

排查过程中尝试过的无效方案

方案 结果 原因
清理 DerivedData + 重新 pod install ❌ 无效 不是缓存问题
移除 -ObjC 链接器标志 ❌ 无效 问题不在 -ObjC
添加 -Xlinker -dead_strip -Xlinker -allow_dead_duplicates ❌ 无效 不能解决内存保护问题
添加 -Wl,-no_fixup_chains ❌ 无效 fixup chains 与此问题无关
升级第三方 SDK(如 AliPlayerSDK) ❌ 无效 预编译 framework 的 section 布局未变
给 protobuf 添加 objc_class_prefix 消除重复类 ❌ 编译错误过多 影响范围太大,不实际
只在主工程添加 -Wl,-no_data_const ❌ 无效 Pod frameworks 也需要该标志
主工程 + 所有 Pod targets 都添加 -Wl,-no_data_const ✅ 解决 所有 image 的 protorefs 都在可写段

补充说明

这个方案有副作用吗?

-no_data_const 会让链接器不使用 __DATA_CONST 优化。__DATA_CONST 的本意是让不需要运行时修改的数据页面变为只读,从而:

  • 减少 dirty memory(脏页)
  • 提高安全性

禁用后,这些数据会放在普通的 __DATA segment,略微增加内存占用,但对 App 功能和性能几乎没有可感知的影响。这是一个合理的 workaround,直到 Apple 在后续 Xcode/iOS 版本中修复这个兼容性问题。

哪些项目容易中招?

  • 使用 use_frameworks!(动态框架)的 CocoaPods 项目
  • 包含预编译二进制 framework(如 AliPlayerSDK、ffmpegkit 等)
  • 存在 ObjC 重复类名的项目(加剧触发概率)
  • ObjC + Swift + Flutter 混编项目

如何验证是否是这个问题?

在 Xcode 调试器中确认:

  1. 崩溃在 libobjc.A.dylib map_images_nolock
  2. 错误码为 EXC_BAD_ACCESS (code=2)(code=2 表示写保护)
  3. 崩溃汇编指令为 str 指令(写入操作)
  4. 崩溃阶段在 "IMAGE TIMES: fix up @protocol references" 附近

gRPC-Core 编译报错

升级到 Xcode 26 后,如果项目依赖了 gRPC(如 protobuf 生成的服务端代码),可能会遇到 C++ template 语法报错。在 Podfile 的 post_install 中添加:

ruby 复制代码
if target.name == 'gRPC-Core'
  target.build_configurations.each do |config|
    flags = config.build_settings['OTHER_CPLUSPLUSFLAGS'] || ['$(inherited)']
    flags << '-Wno-missing-template-arg-list-after-template-kw'
    config.build_settings['OTHER_CPLUSPLUSFLAGS'] = flags
  end
end

完整 Podfile post_install 示例

ruby 复制代码
post_install do |installer|
  # Flutter 相关配置(如有)
  flutter_post_install(installer) if defined?(flutter_post_install)
  
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['ENABLE_BITCODE'] = 'NO'
      
      # Xcode 26: 强制 __objc_protorefs 放入可写 __DATA 段
      other_ldflags = config.build_settings['OTHER_LDFLAGS'] || ['$(inherited)']
      other_ldflags = [other_ldflags] if other_ldflags.is_a?(String)
      unless other_ldflags.include?('-Wl,-no_data_const')
        other_ldflags << '-Wl,-no_data_const'
      end
      config.build_settings['OTHER_LDFLAGS'] = other_ldflags
    end
    
    # gRPC-Core Xcode 26 兼容
    if target.name == 'gRPC-Core'
      target.build_configurations.each do |config|
        flags = config.build_settings['OTHER_CPLUSPLUSFLAGS'] || ['$(inherited)']
        flags << '-Wno-missing-template-arg-list-after-template-kw'
        config.build_settings['OTHER_CPLUSPLUSFLAGS'] = flags
      end
    end
  end
end

总结

Xcode 26 的链接器将 ObjC 协议引用(__objc_protorefs)默认放入只读的 __DATA_CONST segment,而 iOS 18 的 ObjC 运行时在 protocol fixup 阶段假设该内存可写,导致 KERN_PROTECTION_FAILURE 崩溃。

解决方案:给主工程和所有 Pod targets 添加 -Wl,-no_data_const 链接器标志。

希望这篇文章能帮到同样被 Xcode 26 真机崩溃困扰的开发者。如果对你有帮助,请点赞收藏!

相关推荐
Anhty37 分钟前
2026最新免费手机音频处理工具 !!
android·功能测试·ios·智能手机·音视频
2501_915909063 小时前
iOS应用从开发到上架App Store的完整发布流程与步骤指南
android·ios·小程序·https·uni-app·webview
初雪云4 小时前
UniApp 云打包拿到 IPA 后,真正的发布工作才刚开始
ios·自动化·编辑器·产品经理·iphone
Lancker6 小时前
没有 Mac 也能上架 iOS:GitHub Actions 全自动构建 + 签名 + TestFlight 送审实录
macos·ios·github
茶底世界之下6 小时前
晚霞要多暖?用 Awaking 只改色温做一次取舍
ios
恋猫de小郭7 小时前
Genkit Dart 1.0 发布,Flutter 原生的 AI Agent 终于完整了
android·前端·flutter
第10086个小白7 小时前
flutter桌面端打包方式(Inno Setup Compiler)版本
前端·flutter
酒神dnspup8 小时前
多地区网站可用性监控怎么做?DNSPup 从基线到告警的完整实战
运维·ios·云计算·iphone·dns·网络监控
福兮说20 小时前
浏览器里把 iPhone 的 HEIC 转成 JPG:Chrome 解不开、转完大了七成、拍摄时间全丢,六个坑实测
前端·javascript·图像处理·chrome·ios·iphone·heic
陆断枫1 天前
Flutter 3.47 动画引擎盘点:Impeller 全平台,和那些"新特性"
flutter