Release 打包 Swift 编译器崩溃:一个 deinit 救了我的 Archive
环境:Swift 6.3.3 · Xcode 26.5 · iOS Release (
-Osize)
相关 Issue:swiftlang/swift#90150
起因:Debug 能跑,Archive 却挂了
最近在给一个 iOS 项目打 Release 包(Archive)时,编译器直接崩溃,报错类似:
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.
While running pass ... "EarlyPerfInliner" on SILFunction "@$s...CoordinatorCfD" for 'deinit'
...
isCallerAndCalleeLayoutConstraintsCompatible(swift::FullApplySite)
Command SwiftCompile failed with a nonzero exit code
一开始我以为是内存泄漏或循环引用,毕竟栈里出现了 deinit。但仔细看了栈信息后发现:这不是 App 运行时崩溃,而是 Swift 编译器在优化阶段自己挂了。
- Debug(
-Onone):正常编译、正常运行 - Release / Archive(
-Osize):swift-frontend在 SIL 优化阶段崩溃
出问题的代码长什么样?
项目里有多处 SwiftUI + UIKit 桥接 的写法,结构很典型:
struct ScrollViewWithOffset<Content: View>: UIViewRepresentable {
@Binding var scrollOffset: CGFloat
var onOffsetChange: (() -> Void)?
var onRefresh: (() -> Void)?
final class Coordinator: NSObject, UIScrollViewDelegate {
var scrollOffset: Binding<CGFloat>
var onOffsetChange: (() -> Void)?
var onRefresh: (() -> Void)?
weak var scrollView: UIScrollView?
var hosting: UIHostingController<Content>?
// ...
}
}
类似模式还出现在:
HomeView的ScrollViewWithOffsetHomeGoldMoreView的DisableableScrollViewSwiftUIHostController(泛型子类 + 闭包属性)
共同点:
- 泛型
UIViewRepresentable或UIHostingController子类 - 嵌套
Coordinator: NSObject - 成员混合了
Binding、闭包、UIHostingController<Content>?、weak引用 - 没有手写
deinit,依赖编译器自动合成
为什么加 deinit 就能编过?
这是我踩坑时最困惑的一点:手动写 deinit 并不是在修内存泄漏。
对象销毁时,这些属性本来就会被释放。deinit 里写:
deinit {
hosting = nil
scrollView = nil
onChange = nil
onRefresh = nil
}
运行时语义几乎不变,真正改变的是 编译器生成的 SIL(中间表示)。
| 情况 | 编译器行为 |
|---|---|
|
无手写 |
合成复杂释放逻辑 → |
|
有手写 |
SIL 形状更简单 → 优化器绕开 bug → 编译通过 |
所以这是 Swift 6.3.3 优化器的 workaround,不是 Swift 官方推荐的内存管理方式。
项目里 AtlasMainView 的注释也写得很直白:
手动清空 UI 引用,提前释放资源,减少优化器处理逻辑
当时还不完全理解,现在回头看,说的就是这件事。
我的修复方式
在以下几处加了显式 deinit:
1. ScrollViewWithOffset.Coordinator(HomeView)
deinit {
hosting = nil
scrollView = nil
onOffsetChange = nil
onRefresh = nil
}
2. DisableableScrollView.Coordinator(HomeGoldMoreView)
deinit {
hostingVC = nil
}
3. SwiftUIHostController
deinit {
shareCompletion = nil
}
改完后 Release Archive 可以正常打包。
根因:-Osize + 泛型 Coordinator 的合成 deinit
工程里 Debug 用 -Onone,Release 用 -Osize(体积优化):
Debug: SWIFT_OPTIMIZATION_LEVEL = -Onone ✅
Release: SWIFT_OPTIMIZATION_LEVEL = -Osize ❌ 触发崩溃
崩溃发生在编译器的 EarlyPerfInliner 处理泛型 Coordinator 的 deinit(符号后缀 CfD)时。
这是 Internal Compiler Error(ICE),属于工具链 bug,不是业务代码写错。
已向 Swift 官方提交 Issue
我整理了最小复现并提交到 Swift 仓库:
最小复现核心结构:
struct MinimalScrollBridge<Content: View>: UIViewRepresentable {
final class Coordinator: NSObject, UIScrollViewDelegate {
var offset: Binding<CGFloat>
var onChange: (() -> Void)?
weak var scrollView: UIScrollView?
var hosting: UIHostingController<Content>?
// 无 deinit → -Osize 崩溃;有 deinit → 通过
}
}
复现命令:
SDK=$(xcrun --sdk iphoneos --show-sdk-path)
swiftc -Osize -target arm64-apple-ios16.0 -sdk "$SDK" \
-parse-as-library CompilerCrashRepro.swift -o /tmp/out
给其他开发者的建议
如果你也遇到类似情况,可以按下面排查:
1. 先确认是不是编译器崩溃
看日志里有没有:
Please submit a bug reportEarlyPerfInliner...CfDfordeinitswift-frontend栈
有的话,优先怀疑 工具链 bug,不要只在内存管理上打转。
2. 临时 workaround
在泛型 Coordinator 或相关类里加显式 deinit,把引用类型成员置 nil:
deinit {
hosting = nil
someClosure = nil
}
Binding 是 struct,一般不用清。
3. 其他绕过方式(二选一)
- Release 优化从
-Osize改成-O(可能增大包体) - 等 Swift / Xcode 更新修复后再去掉 workaround
4. 帮忙推动修复
- 到 GitHub Issue #90150 回复能否复现
- 或用最小用例向 Swift 提交 bug report
小结
| 误解 | 事实 |
|---|---|
|
不加 |
主要是 编译器优化 bug |
|
|
这里是 改变 SIL、绕过 ICE |
|
Debug 正常就说明代码没问题 |
Debug 不走 |
SwiftUI 与 UIKit 混用时,UIViewRepresentable + UIHostingController 很常见。若 Release 突然编不过、栈里又出现 deinit 和 EarlyPerfInliner,不妨先试试显式 deinit——看起来奇怪,但在 Swift 6.3.3 上确实能救 Archive。
参考链接
如果这篇文章帮到了你,欢迎在 Issue #90150 留言确认是否也能复现,方便 Swift 团队优先修复。
更多推荐


所有评论(0)