我们如何将 iOS App 启动时间降低 60%
Source: Dev.to
介绍
App launch time is your first impression. 如果你的应用打开时间超过 2–3 秒,用户会注意到;达到 5 秒时他们会离开。我们在一个生产环境的 iOS 应用中遇到了这种情况,cold launch time 在中端设备上徘徊在 4.8–5.2 秒左右。经过一次专注的优化冲刺,我们将启动时间降低了≈ 60 %(降至约 2 秒)。
第一步 — 优化前先测量
永远不要猜测——要测量。我们使用了:
- Xcode Instruments → Time Profiler
- App Launch Metric (Xcode Organizer)
- DYLD_PRINT_STATISTICS
- Custom logging for
application(_:didFinishLaunchingWithOptions:)
基准数据
| 指标 | 之前 |
|---|---|
| 冷启动 | 5.1 s |
| 热启动 | 2.7 s |
| 主线程阻塞 | 3.4 s |
洞察 – 大部分时间花在第一帧渲染之前,这意味着启动工作阻塞了主线程。
第2步 — 找出阻塞主线程的原因
- 启动时的繁重依赖注入
- 启动期间的数据库迁移
- 同步网络请求
- 大型 storyboard 初始化
- 动态框架数量过多
所有这些都发生在首个界面出现之前。
优化让我们提升了 60 %
1. 延迟非关键工作 (最大收益)
之前
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
setupAnalytics()
migrateDatabase()
preloadImages()
fetchRemoteConfig()
return true
}
之后
DispatchQueue.global(qos: .background).async {
self.setupAnalytics()
self.migrateDatabase()
self.preloadImages()
self.fetchRemoteConfig()
}
// 或者更晚一点:
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// 非关键工作
}
结果 – 立即节省约 1.8 秒。
2. 延迟加载依赖
之前
let networkManager = NetworkManager()
let cacheManager = CacheManager()
let analytics = Analytics()
之后
lazy var networkManager = NetworkManager()
结果 – 节省约 400 毫秒(只有在使用该功能时才会产生开销)。
3. 降低 Storyboard 复杂度
初始 storyboard 包含 20 多个视图控制器、繁重的 Auto Layout、自定义字体以及嵌套的导航。
解决办法
- 将 storyboard 拆分为更小的片段。
- 使用轻量级启动画面。
- 将重量级视图迁移到代码创建的 UI。
结果 – 节省约 300–500 毫秒。
4. 优化动态框架
每个动态框架都会增加启动开销(dyld 链接、符号解析)。我们当时有 18 个框架。
措施
- 合并体积较小的框架。
- 将部分框架转换为静态库。
- 删除未使用的 pods。
结果 – 节省约 700 毫秒。
5. 将数据库迁移移出启动流程
我们在每次启动时都迁移 SQLite。
修复
- 仅在模式版本变化时才运行迁移。
- 在首屏出现后、在后台队列中执行迁移。
结果 – 节省约 600 毫秒。
6. 图片与资源优化
问题
- 大尺寸 PNG 和不必要的 @3x 资源。
- 启动时预加载图片。
解决办法
- 将资源转换为 WebP/HEIF。
- 按需加载图片。
- 移除预加载。
结果 – 节省约 200–300 毫秒。
最终指标
| 指标 | 之前 | 之后 |
|---|---|---|
| 冷启动 | 5.1 s | 2.0 s |
| 热启动 | 2.7 s | 1.1 s |
| 主线程阻塞 | 3.4 s | 0.9 s |
总体提升: ≈ 60 % 更快的启动。
关键经验教训
做
- 推迟所有非关键的事务。
- 延迟加载依赖。
- 使用 Instruments 进行测量。
- 最小化动态框架的使用。
- 保持启动画面轻量化。
不要
- 在启动时调用 API。
- 在主线程上迁移数据库。
- 过早初始化所有服务。
- 加载庞大的 storyboard。
- 阻塞主线程。
快速启动优化检查清单
- 使用轻量级启动画面。
- 延迟加载服务。
- 移除不必要的框架。
- 推迟分析初始化。
- 在后台执行数据库工作。
- 启动时避免使用庞大的依赖注入容器。
- 使用 Instruments 定期进行性能分析。
最后思考
启动时间直接影响:
- 留存
- 评分
- 感知质量
- 转化率
用户在几秒钟内(真的)就会对你的应用下判断。把启动性能视为一个功能,而不是事后才考虑的事项。通过智能地延迟工作、懒加载以及去除臃肿代码,我们在不改变核心功能的前提下,实现了 60 % 的性能提升。