优化 App 体积与运行时性能
Improve app size and runtime performance
2022年6月6日
一句话判断
Xcode 14 在编译器和运行时层面做了四项透明优化——协议检查预计算、objc_msgSend stub 瘦身、retain/release 指令精简、autorelease elision 加速——你不需要改任何代码,重新编译就能受益。
这场 Session 讲了什么
Clang 和 Swift 编译器团队的 Ahmed 讲解了四项底层优化,这些优化对开发者完全透明:不需要新 API、不需要改代码、不需要调整构建设置。
四项优化分别是:Swift 协议检查从运行时提前到 dyld closure 阶段预计算;objc_msgSend 调用通过 selector stub 机制从 12 字节缩减到 8 字节;retain/release 调用通过 stub 从 8 字节缩减到 4 字节;autorelease elision 机制变得更加高效和紧凑。每项优化都有明确的作用条件和启用方式。
值得深挖的点
协议检查能占启动时间的一半。 真实 App 的测量数据显示,Swift 协议检查在启动时间中占比可达数百毫秒。新的预计算机制将这些工作移到 dyld closure 中,在 App 启动前完成。在 iOS 16/tvOS 16/watchOS 9 上,即使是已发布的 App 也能自动受益。
Selector stub 的两种模式。 Xcode 14 默认使用 balance 模式(兼顾体积和性能),也可以通过 -objc_stubs_small linker flag 切换到纯体积优化模式(最大节省但多一次间接调用)。除非极度体积敏感,否则推荐默认模式。
autorelease elision 的优化思路。 当编译器可以证明一个对象不会逃逸到 autorelease pool 时,直接跳过 autorelease/release 操作。新的优化让这个检查更快、生成的代码更小。
代码片段
# Xcode 14 默认启用所有优化,无需额外配置
# 如果极度体积敏感,可启用纯体积优化的 msgSend stub
# 在 Other Linker Flags 中添加:
-objc_stubs_small
# 查看二进制体积变化
size MyApp_before
size MyApp_after
// 协议检查优化:运行时 → dyld closure 预计算
protocol CustomLoggable {
var customLogString: String { get }
}
struct Event: CustomLoggable {
var name: String
var date: Date
var customLogString: String { "\(name) on \(date)" }
}
// as? 协议检查在 iOS 16 上更快
func log(_ value: Any) {
if let loggable = value as? CustomLoggable {
print(loggable.customLogString)
}
}
最佳实践
- 升级到 Xcode 14 重新编译即可获得大部分优化。 msgSend stub 优化在旧版 OS 部署目标上也有效,协议检查和 retain/release 优化需要 iOS 16+ 运行时。
- 不要盲目开启
-objc_stubs_small。 默认模式已提供 2% 代码体积减少,纯体积模式额外节省有限但牺牲性能。 - retain/release 优化需要 iOS 16+ 部署目标。 因为涉及运行时支持,不像 msgSend stub 纯粹是编译期优化。
- 混合 Swift 和 Objective-C 的项目受益最大。 msgSend 和 retain/release 调用在 OC 互操作中无处不在。
还有什么值得关注
- 推荐观看 “App Startup Time: Past, Present, and Future” 理解 dyld closure 的工作原理
- 二进制体积减少 2-4% 对于大型 App 来说非常显著,尤其是 App Clip 等 size-sensitive 场景
- autorelease elision 的优化对使用 ARC 的 Objective-C 代码效果最明显
- 这些优化是 Swift 和 Clang 编译器团队持续投入的方向,后续版本还会有更多改进