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. 重复类加剧问题

虽然重复类(StatisticsKeychain)本身只是警告,但它们会导致运行时需要处理更多的 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 也加上这个标志。

Podfilepost_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 真机崩溃困扰的开发者。如果对你有帮助,请点赞收藏!

相关推荐
2601_949593651 小时前
Flutter for Harmony 跨平台开发实战:超形状与超椭圆——参数方程的形态边界
flutter
Swift社区1 小时前
Flutter 中如何优雅地处理复杂表单
前端·flutter·前端框架
不爱吃糖的程序媛1 小时前
Flutter-OH 三方库 devicelocale 鸿蒙适配
flutter·华为·harmonyos
2501_9219308310 小时前
进阶实战 Flutter for OpenHarmony:Isolate 多线程计算系统 - 并发任务处理实现
flutter
加农炮手Jinx10 小时前
Flutter for OpenHarmony 实战:JWT — 构建安全的无状态认证中心
网络·flutter·华为·harmonyos·鸿蒙
左手厨刀右手茼蒿10 小时前
Flutter for OpenHarmony: Flutter 三方库 hashlib 为鸿蒙应用提供军用级加密哈希算法支持(安全数据完整性卫士)
安全·flutter·华为·c#·哈希算法·linq·harmonyos
王码码203510 小时前
Flutter for OpenHarmony: Flutter 三方库 cryptography 在鸿蒙上实现金融级现代加解密(高性能安全库)
android·安全·flutter·华为·金融·harmonyos
编程之路从0到111 小时前
ReactNative新架构之iOS端TurboModule源码剖析
react native·ios·源码阅读
亚历克斯神11 小时前
Flutter for OpenHarmony:Flutter 三方库 yaml_edit 精准修改 YAML 文件内容(保留注释与格式的编辑神器)
android·flutter·华为·harmonyos