問題現象:Xcode 正常,冷啟動閃退

最近在開發 Flutter iOS App 時遇到一個詭異的問題:透過 Xcode 按下 Run 按鈕啟動 App 完全正常,但當我停止 Debugger、從多工畫面滑掉 App、再從主畫面點擊圖示重新開啟時,App 只顯示 Launch Screen 就立刻閃退。

這個問題讓我走了不少彎路,最後發現答案簡單得令人意外。


誤導方向:UIScene 與 Plugin 註冊

由於 Xcode 26 開始顯示 UIScene lifecycle will soon be required 警告,我最初懷疑是 UIScene 遷移不完整導致的問題。接著又懷疑是 Plugin 註冊的 race condition,甚至在 GeneratedPluginRegistrant.m 中加入大量 debug log 來追蹤每個 Plugin 的註冊狀態。

嘗試過的「修復」包括:

  • 完整實作 SceneDelegateFlutterSceneDelegate
  • 在 Flutter Engine 初始化前加入 0.5 秒延遲
  • 逐一排除可能有問題的 Plugin

這些方向全部是錯的。


找到根本原因:iOS Console Log

關鍵轉折點是查看 iOS 的 Console log。使用 Xcode 的 Devices and Simulators > Open Console 或 macOS 的 Console.app 連接 iPhone,重現冷啟動閃退後,看到了這段關鍵訊息:

[ERROR:flutter/runtime/ptrace_check.cc(75)] Could not call ptrace(PT_TRACE_ME): Operation not permitted

Cannot create a FlutterEngine instance in debug mode without Flutter tooling or Xcode.

To launch in debug mode in iOS 14+, run flutter run from Flutter tools,
run from an IDE with a Flutter IDE plugin or run the iOS project from Xcode.
Alternatively profile and release mode apps can be launched from the home screen.

真相大白:這根本不是程式碼問題,而是 iOS 14+ 的安全機制。


技術原理:ptrace 與 Debug 模式

為什麼 Debug 模式需要 Debugger?

iOS 14 開始,Apple 強化了 ptrace 系統呼叫的權限控制。ptrace 是 Unix 系統用於程序追蹤和除錯的機制,Debug 模式的 Flutter App 依賴它來實現:

  • Hot Reload / Hot Restart:即時更新程式碼
  • Dart VM 連接:與 Flutter DevTools 通訊
  • 斷點除錯:暫停執行、檢查變數

當 App 嘗試呼叫 ptrace(PT_TRACE_ME) 但沒有 Debugger 連接時,iOS 會拒絕這個請求並終止 App。

不同建置模式的差異

建置模式執行方式ptrace 需求可獨立運行
DebugJIT 編譯需要
ProfileAOT 編譯 + 效能追蹤不需要
ReleaseAOT 編譯 + 最佳化不需要

Debug 模式使用 JIT(Just-In-Time)編譯,需要 Dart VM 持續運行並與開發工具通訊。Release 模式則是 AOT(Ahead-Of-Time)編譯成原生機器碼,完全不需要 Dart VM。


解決方案:使用 Release 模式

如果需要在沒有 Xcode 連接的情況下測試 App(例如給 QA 測試或自己日常使用),必須使用 Release 模式建置:

# 建置 Release 版本
flutter build ios --release

# 或使用 Profile 模式(保留部分除錯資訊)
flutter build ios --profile

建置完成後,可以透過 Xcode 安裝到裝置:

# 使用 devicectl 安裝(Xcode 15+)
xcrun devicectl device install app \
  --device <device-id> \
  build/ios/iphoneos/Runner.app

為什麼容易踩到這個坑?

這個問題之所以難以發現,是因為開發流程中的盲點:

  1. 開發時總是透過 Xcode 或 flutter run 啟動:Debugger 自動連接,問題不會發生
  2. 測試時也是如此:很少有人會特意停止 Debugger 後再測試冷啟動
  3. 錯誤訊息不會出現在 Xcode Console:必須查看 iOS 系統 Console 才看得到

經驗法則:當 iOS App 出現「Xcode 正常但獨立運行異常」的情況,第一步應該查看 iOS Console log,而不是猜測程式碼問題。


結論

問題答案
為什麼要用 Release 模式?iOS 14+ 的 ptrace 安全限制,Debug 模式必須有 Debugger 連接
不這樣做會怎樣?每次冷啟動都會閃退,只能透過 Xcode 啟動
未來如何避免?分發測試版本時一律使用 Release 或 Profile 模式

這個問題的答案雖然簡單,但診斷過程卻充滿了誤導。記住:Flutter Debug build 在 iOS 上無法獨立運行,這是設計如此,不是 bug。


參考資源