# React Native iOS 排查记录:从 Yoga 编译报错,到模拟器反复白屏

React Native iOS 排查记录:从 Yoga 编译报错,到模拟器反复白屏

排查时间:2026 年 10 月 12---13 日。本文记录一次旧版 React Native 项目在较新 Xcode 环境中的实际排查过程。最终恢复了模拟器运行,并保存了绕过调试器暂停问题的启动方案;Xcode/LLDB 的底层原因仍未完全确认。

这次遇到的问题很容易让人误以为是同一个故障:先是编译时出现 Yoga 报错,接着应用启动后提示定位模块没有链接,最后又变成没有红屏提示的白屏。期间还把最低支持系统从 iOS 11 改成了 iOS 13,更增加了判断难度。

最后发现,需要分别处理三个问题:Yoga 的旧语法触发了新编译器的检查、应用连接了另一个项目的 Metro 服务,以及原生调试器在 Rosetta 模拟器里反复暂停应用。

1. 排查环境

项目 本次环境
React Native 0.68.5
Xcode 26.4
模拟器系统 iOS 26.4
运行目标 iPhone Pro Max(Rosetta)
CocoaPods 1.11.3,由项目 Bundler 管理
应用最低支持版本 从 iOS 11 调整为 iOS 13

这些结论只针对本次环境。如果其他项目出现相似白屏,仍需要结合实际日志和进程状态判断。

2. 第一个问题:Yoga 的空格为什么会导致编译失败?

最开始,Xcode 在下面的文件里报错:

text 复制代码
node_modules/react-native/ReactCommon/yoga/yoga/YGValue.h:77:27
Identifier '_pt' preceded by whitespace in a literal operator declaration is deprecated

检查源码后,发现 Yoga 中存在这样的 C++ 写法:

cpp 复制代码
inline YGValue operator"" _pt(long double value) {
  return YGValue{static_cast<float>(value), YGUnitPoint};
}

这里定义的是自定义字面量运算符,用来表达点数、百分比等布局数值。新版 Clang 会对 operator"" 与后缀之间带空格的写法发出弃用警告,对应检查项是 -Wdeprecated-literal-operator。Clang 官方诊断说明

为什么警告变成了红色错误?因为项目中 Yoga 的编译参数包含:

text 复制代码
-Werror

它会把警告当作错误处理。因此,虽然布局逻辑没有变化,这种旧语法仍然可能阻断编译。

修复方式

去掉 "" 和后缀之间的空格:

cpp 复制代码
// 修改前
operator"" _pt
operator"" _percent

// 修改后
operator""_pt
operator""_percent

本项目的这个头文件共有 6 处需要调整,包含运算符定义和内部调用。

直接修改 node_modules 只能修好当前安装目录。为了避免重装依赖后丢失,新增了 scripts/fix-yoga-literals.js,并在 package.json 中配置:

json 复制代码
{
  "scripts": {
    "postinstall": "node scripts/fix-yoga-literals.js"
  }
}

脚本的核心处理是:

js 复制代码
const patched = source.replace(
  /operator""[ \t]+(_pt|_percent)\b/g,
  'operator""$1',
);

脚本只在内容发生变化时写回文件,重复执行不会继续修改已修好的内容。如果安装依赖时禁用了生命周期脚本,可以手动执行:

bash 复制代码
node scripts/fix-yoga-literals.js

修复后,使用本机 Clang 对该头文件及整数、小数形式的点数和百分比字面量做了编译检查,开启 -Wall -Werror 后检查通过。这个验证说明该处语法问题已经消除,完整应用是否能启动还需要继续验证。

3. 第二个问题:提示定位模块没链接,实际却连错了项目

应用运行后出现红屏:

text 复制代码
The package '@react-native-community/geolocation' doesn't seem to be linked.

报错建议执行 pod install 并重新构建。但检查项目依赖后发现了矛盾:

  • 当前项目使用的是 react-native-amap-geolocation。
  • 当前项目没有安装 @react-native-community/geolocation。
  • 高德定位已经存在于 Podfile.lock 和 Pods 工程中。

因此,下一步应该确认"当前应用到底加载了哪个项目的 JS",而不是立即安装红屏里提到的包。

检查 8081 端口属于哪个项目

Metro 是 React Native 开发时提供 JS 代码的本地服务,通常监听 8081 端口。先查监听进程:

bash 复制代码
lsof -nP -iTCP:8081 -sTCP:LISTEN

记下输出中的 PID,再查看该进程的工作目录:

bash 复制代码
# 将 12345 替换为上一条命令查到的 PID
lsof -a -p 12345 -d cwd -Fn

这次查到的目录属于另一个 React Native 项目。那个项目恰好安装了红屏提到的定位包,其依赖代码中也包含相同报错和调用栈里的函数名。

这些证据指向:当前 iOS 应用连接了另一个项目的 Metro 服务,加载的 JS 与应用内编译好的原生模块不匹配。

处理方式

关闭另一个项目的 Metro 服务,在当前项目根目录启动:

bash 复制代码
npm start -- --reset-cache

然后重新加载应用,并再次确认 8081 对应的工作目录。

本次排查的关键是进程所属项目。重置缓存可以帮助清理旧缓存,但无法替代"确认服务属于正确项目"这一步。

4. 第三个问题:白屏时,应用实际上被调试器暂停了

后续应用不再显示定位模块红屏,却出现了白屏。Xcode 顶部显示:

text 复制代码
Paused scm on iPhone Pro Max

线程停在:

text 复制代码
dyld`lldb_image_notifier
Thread 2: trace

其中一次暂停发生在 AXBundleLoadQueue,另一次发生在主线程。通过 LLDB 查询,得到的停止原因是:

text 复制代码
stop reason = trace

为什么暂停会表现成白屏?

LLDB 是 Xcode 使用的原生调试器。它附加到应用后,可以暂停进程,让开发者查看变量、调用栈和断点。

一旦进程被暂停,页面渲染和事件处理也会停下来。如果此时页面尚未完成绘制,模拟器就可能一直停留在白色背景。

最初点击"继续执行"后,应用能够恢复。但后来重新启动又出现相同暂停,说明仅恢复一次运行还不够,需要进一步隔离调试器的影响。

日志提供了哪些证据?

新日志中可以看到:

text 复制代码
Running application scm
componentDidMount-app.js
HomeHomeHome

同时还有接口返回和首页相关输出。这说明 JS 已经执行到了应用挂载、首页渲染等路径。虽然这些日志不能单独证明页面一定显示成功,但与 Xcode 的暂停状态结合后,调试器成为了明确的排查方向。

日志里还出现了 dSYM、线程性能检查和模拟器系统服务等提示。它们值得分别理解,但这次没有证据表明其中某一条就是反复白屏的直接原因。

用实际启动验证调试器的影响

本次进行了三步验证:

  1. 在暂停状态下分离 LLDB,应用随后恢复首页。
  2. 不附加 LLDB,终止并重新启动应用,首页正常显示。
  3. 创建不附加 LLDB 的 Xcode 运行方案,用该方案重新构建并启动,首页再次正常显示。

这组结果支持:本次白屏与 LLDB 暂停有关;不附加调试器可以绕过目前的问题。

Apple 开发者论坛也有 Xcode 26 停在 lldb_image_notifier 的类似报告,其中有开发者反馈问题与 Apple Silicon 上的 Rosetta 模拟器有关。相关讨论

结合本项目使用的 Rosetta 运行目标,证据指向 Xcode/LLDB 与 Rosetta 模拟器之间的调试兼容问题。不过,论坛反馈和本次验证都不足以确认具体底层缺陷,也不能据此认定所有同类白屏都是相同原因。

5. 最终处理:保存一套不附加 LLDB 的运行方案

为了避免每次启动都手动处理暂停,保留原来的 scm 方案,并新增了 scm-simulator。

Scheme 是什么?

Scheme 可以理解成"编译和启动应用的一套设置"。它决定使用哪个 Target、哪种构建配置,以及启动时是否附加调试器。

项目创建时,Xcode 通常已经生成默认 Scheme。只有需要保存不同启动方式时,才有必要再复制一份。

本次两个方案的主要区别如下:

运行方案 构建配置 启动时附加 LLDB 使用场景
scm Debug 是 需要 iOS 原生断点调试时使用
scm-simulator Debug 否 当前环境下,在模拟器里运行页面、开发 JS

Debug 构建与附加 LLDB 是两个不同设置。 关闭原生调试器后,应用仍使用 Debug 构建,JS 热更新和日志仍然可用,但该方案无法直接使用 LLDB 原生断点。

在 Xcode 中创建

本次使用的是复制现有方案的方法:

  1. 选择 Product → Scheme → Edit Scheme...。
  2. 点击 Duplicate Scheme,复制当前方案。
  3. 将副本命名为 scm-simulator。
  4. 在新方案中选择 Run → Info。
  5. 保持 Build Configuration = Debug。
  6. 取消勾选 Debug executable。
  7. 勾选 Shared,将方案保存为项目内可共享的配置。
  8. 关闭编辑窗口。

生成的文件是:

text 复制代码
ios/scm.xcodeproj/xcshareddata/xcschemes/scm-simulator.xcscheme

其中启动配置的关键内容为:

xml 复制代码
<LaunchAction
   buildConfiguration = "Debug"
   selectedDebuggerIdentifier = ""
   selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
   ...>

以后怎样切换?

点击 Xcode 顶部、设备名称左边的方案名称,在列表中选择:

  • 日常在模拟器里运行:scm-simulator。
  • 需要 iOS 原生断点:scm。

然后选择运行设备,点击 ▶。

scm-simulator 是自定义名字,名字本身不会限制只能选择模拟器。它保存的是"不附加 LLDB"的启动设置,使用时仍需确认右侧运行目标。

这个处理属于绕过办法。切回原方案、继续在相同 Rosetta 环境附加 LLDB 时,仍可能再次遇到暂停。要彻底解决,后续还需要验证更新的调试环境,或在项目依赖支持的情况下评估普通 arm64 模拟器。

6. iOS 11 改成 13,与这次白屏有什么关系?

排查期间,应用 Target 的最低系统版本被从 11.0 调整为 13.0,但 Podfile 当时仍然是:

ruby 复制代码
platform :ios, '11.0'

最低支持版本表示"应用最早能运行在哪个 iOS 版本",并不表示把编译 SDK 或模拟器系统切换成该版本。Apple 构建设置说明

决定最低支持 iOS 13 后,同步修改了 Podfile:

ruby 复制代码
platform :ios, '13.0'

随后重新安装 Pods,成功更新了集成配置。Podfile.lock 中依赖版本没有变化,只更新了 Podfile 校验值。CocoaPods 的 platform 配置说明

但同步后,LLDB 暂停仍然出现。因此,目前没有证据说明白屏是从 iOS 11 改成 13 导致的,执行 pod install 也没有解决调试器暂停。

另外,应用最低版本高于某个依赖的最低版本,通常是允许的。同步 Podfile 不代表必须把每一个第三方 Pod 的最低版本都强制设成 13。

为什么直接执行 pod install 找不到命令?

当前项目通过 Bundler 管理 CocoaPods,直接执行时出现:

text 复制代码
zsh: command not found: pod

项目中实际使用并安装成功的命令是:

bash 复制代码
# 从项目根目录进入 ios
cd ios
bundle exec pod install

bundle exec 会使用项目 Gemfile 约束的工具环境。如果换到新电脑,还需要先准备项目要求的 Ruby 和 Bundler 依赖。

7. 这次排查留下的经验

遇到相似问题,可以按下面的顺序缩小范围:

  1. 先判断阶段。 编译失败、启动红屏和运行白屏需要分别找证据。
  2. 核对报错中的依赖是否属于当前项目。 如果包名与项目依赖对不上,检查 Metro 进程的工作目录。
  3. 白屏时先看 Xcode 是 Running 还是 Paused。 暂停状态下,页面无法继续更新。
  4. 把日志与实际界面结合。 出现首页日志不等于首页已经显示,仍要检查模拟器。
  5. 用对照实验验证推断。 本次比较了附加 LLDB、分离 LLDB和不附加 LLDB重新启动的行为。
  6. 区分修复与绕过。 Yoga 语法已修复;Metro 连接需确认项目归属;新的 Scheme 绕过了调试器问题,底层兼容原因仍待进一步确认。

最终,本次保留下来的项目改动包括 Yoga 安装后修复脚本、iOS 13 的 Podfile 配置,以及 scm-simulator 共享运行方案。最后一项已经通过 Xcode 构建启动和模拟器首页显示验证。

相关推荐
song50118 小时前
React Native for OpenHarmony 实战:三方库 react-native-parallax-scroll-view 的鸿蒙化适配指南
分布式·react native·react.js·transformer·harmonyos
song50120 小时前
React Native for OpenHarmony 实战:三方库 react-native-ratings 的鸿蒙化适配指南
javascript·react native·react.js·分类·harmonyos
song5011 天前
React Native for OpenHarmony 实战:三方库 react-native-popup-menu 的鸿蒙化适配指南
css·深度学习·react native·react.js·transformer·harmonyos
梦想的颜色2 天前
【编程实战】AI 时代 APP 开发全栈硬核指南:技术选型 + AI 架构 + 模型落地全维度决策
python·flutter·react native·react.js·ai·桌面应用·milvus
song5012 天前
React Native for OpenHarmony 实战:三方库 react-native-css-transformer 的鸿蒙化适配指南
css·人工智能·深度学习·react native·transformer
enjoywindstorm5 天前
移动端原生框架对比评测
react native
传奇开心果编程5 天前
【声明式UI开发实用技术学与练】第8课 状态提升与下放
学习·flutter·react native·ui·swiftui·composer
传奇开心果编程6 天前
【现代声明式UI学与练】第4课 列表渲染与 key——如何高效渲染列表、key 的作用、列表重排时的状态保持
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程7 天前
【现代声明式UI学与练】第7课 组件通信——父传子、子传父、跨层级通信、状态提升与 Context
学习·flutter·react native·ui·swiftui·android jetpack