iOS 冷启动优化(Launch Time)
一、启动类型(一句话区分)
- 冷启动:App 进程未创建,系统从零加载(面试官核心关注点)
- 热启动:App 已在后台,仅恢复前台状态(优化空间极小)
二、冷启动核心流程(三阶段)
冷启动耗时 = 系统加载 + 动态库 + runtime 初始化 + main 函数执行 + 首屏渲染前业务
标准三阶段:
- dyld 阶段(加载动态库、重定位、绑定)
- Runtime 阶段(ObjC 类注册、分类加载、load 方法、method swizzling)
- main() 阶段(执行 main → didFinishLaunchingWithOptions → 首屏渲染)
总原则:越早的阶段,优化收益越大,影响越大。
也可以分成两个阶段
-
pre-main:dyld 加载动态库、rebase/bind、Objc 类+分类注册、
+load方法。 -
main 后:
didFinishLaunching中的各种 SDK 初始化、首页 UI 构建、数据请求。
1. pre-main 优化
- 减少动态库:合并、尽量用静态库。
-
清理
+load:用+initialize或启动后懒加载替换。 - 减少 ObjC 类、分类、方法数(合并无用类)。
-
二进制重排(Binary Reorder):
- 使用
Order File将启动期调用的符号集中到二进制的连续区域,减少缺页中断。 - 生成方法:hook
objc_msgSend获取启动调用栈(或借助 llvm 插桩)。
- 使用
2. main 后优化
-
延迟初始化:非核心 SDK 挪到首屏渲染后再初始化(
dispatch_after或监听 RunLoop 空闲)。 - 懒加载首页:先展示 placeholder 或快照,再做复杂网络请求。
- 代码瘦身:无用代码/资源移除,图片预合成为纹理。
三、核心优化策略(最专业、最精简、最高收益版)
1. dyld 动态库优化(收益极高)
动态库加载会产生大量 Page Fault(缺页中断),每个动态库会增加 6~8ms 耗时。
优化方案:
- 尽量使用静态库替代动态库
- 合并多余的私有动态库,减少总数
- 系统动态库(UIKit、Foundation)已被系统缓存,无需优化
2. 清理 + 延迟 + 异步启动初始化(收益极高)
didFinishLaunching 是启动耗时重灾区,必须瘦身。
禁止在启动路径执行:
- 日志、埋点、非必要 SDK
- 数据库初始化、配置拉取、非首屏资源
- 大量 I/O、网络请求、同步锁
优化方案:
- 核心业务必须同步(如路由、基础配置)
- 非核心业务延迟执行(
dispatch_async/ runloop 空闲) - 可异步的全部放入子线程(不阻塞主线程)
- 删除无用的
+load方法(全部挪到+initialize或手动调用)
3. 二进制重排(启动优化天花板)
这是行业公认收益最大的优化手段,能降低 30%~60% 缺页中断。
原理
启动时函数分散在不同内存页,触发大量 Page Fault,重排后让启动函数紧凑排列在连续页,大幅减少中断。
方案
- 通过 Clang 插桩(
-fsanitize-coverage=trace-pc-guard)收集启动调用函数 - 生成
.order符号排序文件 - 编译时让链接器按照 order 文件排列代码段
效果
冷启动速度提升 30%~60%,是大厂标配优化。
4. 清理 +load 方法(非常关键)
- 所有
+load方法都会在 Runtime 阶段主线程同步执行 - 分类越多、load 越多,启动越慢
- 优化:全部干掉,替换为手动初始化或惰性初始化
5. 首屏资源预加载 & 懒加载
- 提前异步解码首屏图片
- 首屏必须资源预加载,非首屏延迟加载
- 避免启动时大量 IO、大量绘图、大量布局计算
四、最终总结(面试背诵版)
iOS 冷启动优化主要围绕减少动态库、清理 load 方法、延迟非核心初始化、二进制重排、降低缺页中断五大方向,其中二进制重排是效果最显著的手段,清理启动路径阻塞代码是最基础也最必须的优化。
启动优化
经典面试题
“怎么精确测量启动时间?有哪些环境变量可用于分析?”
答案:DYLD_PRINT_STATISTICS 打印 pre-main 各阶段耗时;Time Profiler 定位 main 后耗时;MetricKit 线上监控。