2025 年初 iOS 18.4 正式发布后,许多 Flutter 开发者(尤其是用物理 iOS 设备调试的)运行 flutter run(默认 debug 模式)时,app 会立即崩溃,日志显示类似:

1
../../../flutter/third_party/dart/runtime/vm/virtual_memory_posix.cc: 428: error: mprotect failed: 13 (Permission denied)
  • 症状细节:崩溃发生在 Dart VM 初始化阶段,具体是 JIT(Just-In-Time)编译器试图动态生成/修改机器码时。热重载(hot reload)和热重启也失效。模拟器(Simulator)不受影响,因为它运行在 macOS 上,不受 iOS 沙箱限制。
  • 影响范围:仅限 iOS 物理设备(真机),Android 和其他平台正常。Flutter 版本在 3.32.x(stable 通道)及更早的都受波及,beta/master 通道在 2025 年 7 月后逐步修复。调试是 Flutter 的核心优势(快速迭代),这直接打断了开发流程,许多人被迫用 release 模式(flutter run –release)或 profile 模式(flutter run –profile)作为临时替代,但这些模式不支持热重载,开发效率大打折扣。

肯定是和升级相关,分析一下为什么出现这个问题

这100%是 iOS 18.4 的“锅”——Apple 为提升系统安全,强化了内存保护机制。具体分析如下:

  • 技术根源:Dart VM 在 debug 模式下使用 JIT 编译:它会将 Dart 字节码动态转换为 arm64 机器码,写入 app 的可执行内存段(executable memory)。这个过程需要:
    1. 将内存权限从 RX(Read-Execute,只读执行)临时切换到 RW(Read-Write,可读写),用 mprotect 系统调用修改。
    2. 写入新代码。
    3. 切换回 RX 以执行。 这在 iOS 17 及更早版本中可行,因为 get-task-allow 标志允许调试 app 绕过代码签名和内存保护(开发者模式下)。
  • iOS 18.4 的变化:Apple 在 18.4 中关闭了这个绕过,理由是安全:防止恶意 app(或调试器)篡改运行时内存,降低越狱或侧信道攻击风险。结果,mprotect 调用直接返回 EPERM(权限拒绝,错误码 13),导致 Dart VM 崩溃。Apple 在 beta 2 中短暂“走回”(移除限制),但 5 月确认会永久恢复。
  • 为什么 Flutter 受影响最大?Flutter/Dart 是跨平台框架,依赖 JIT 来实现热重载(实时更新 UI/逻辑而不重启 app)。原生 iOS app(Swift/Objective-C)多用 AOT(Ahead-of-Time)编译,不需运行时修改内存,所以影响小。其他框架如 React Native 也类似,但 Flutter 的 JIT 深度嵌入,问题更突出。
  • 更深层原因:这是平台演进的“副产品”。Apple 优先安全(响应全球隐私法规),但未充分考虑开发者工具链,导致生态断层。类似问题在 Android 上也偶现,但 Google 更注重开发者友好。

Flutter 团队是如何修复的呢?

Dart/Flutter 团队(Google 主导,开源协作)响应迅速,从 2025 年 2 月 issue 开立,到 7 月在 master/beta 通道落地修复,11 月确认稳定。修复不是简单补丁,而是架构级重构,避免对 Apple 机制的依赖。核心方案:

  • 修复路径
    • 短期 workaround(3 月落地):用 LLDB(Xcode 调试器)手动重映射内存(创建 RW 和 RX 的双视图),或切换到 simarm64 配置(模拟 arm64 架构)。这些在 issue 中有 Gist 示例,但不适合日常开发。
    • 长期方案(7 月 master/beta 实现):复兴 Dart 字节码(DBC)解释器。
      • 怎么做:Dart VM 原本在 2010 年代支持字节码解释,但为性能弃用。现在团队定制一个高效的 DBC 解释器,专为 iOS debug 构建设计。它直接解释字节码(无需 JIT 生成机器码),绕过内存权限翻转。解释器优化包括:
        • 内联缓存(inline caching)加速热点代码。
        • 支持调试特性(如断点、栈追踪),并与热重载集成。
        • 比 WASM(WebAssembly)解释器更快(Dart 团队测试显示,启动时间仅增加 5-10%,热重载延迟 <50ms)。
      • 代码变更:核心在 Dart SDK 的 runtime/vm/interpreter.cc 和 Flutter 引擎的 iOS 构建脚本中。相关 commit 见 Flutter issue #172686。
    • 通道 rollout
      • 7 月:master/beta 支持完整 debug(包括热重载)。
      • 11 月:回溯到 stable 3.35.x+(你的 3.38.4 就是受益者),但早期 stable 如 3.32.x 无 backport(变更太大,风险高)。
      • 12 月现状:全通道稳定,iOS 19(假设已出)也兼容。