iOS App启动优化(四):编译期插桩 && 获取方法符号
iOS App启动优化(五):收集符号 && 生成 Order File
冷启动和热启动
- 冷启动:指
APP被后台kill后重新启动APP,这种启动方式叫做冷启动。 - 热启动:
APP的状态由running切换为suspend,APP没有被kill仍然在后台运行。再次把APP切换到前台,这种启动方式叫热启动。
启动时间的组成
- 启动时间的划分可以把
main()函数作为关键点分割成两块 t1阶段,main()之前的处理所需时间,称为pre-maint2阶段,main()及main()之后处理所需时间
t1阶段:pre-main
t2阶段
t2阶段耗时的主要是业务代码
推荐 BLStopwatch,这个工具可以打点统计业务耗时
本部分优化根据各自业务需求自行处理
Xcode 测量 pre-main 时间
通过添加环境变量可以获取到pre-main阶段的时间
DYLD_PRINT_STATISTICS
Xcode 中提供了测量 pre-main 的时间 Edit scheme -> Run -> Auguments 添加环境变量 DYLD_PRINT_STATISTICS,value设为YES。
启动以后可以看到启动时长
加载dylib
分析每个dylib(大部分是系统的),找到其Mach-O文件,打开并读取验证有效性;找到代码签名注册到内核,最后对dylib的每个segment调用mmap()。
在dylib的加载过程中系统为了安全考虑引入了ASLR(Address Space Layout Randomization)技术和代码签名。
ASLR技术:镜像Image、可执行文件、dylib、bundle在加载的时候会在其指向的地址(preferred_address)前面添加一个随机数偏差(slide),防止应用内部地址被定位。
rebase/bind
dylib加载完成之后,它们处于相互独立的状态,需要绑定起来。
Rebase将镜像读入内存,修正镜像内部的指针,性能消耗主要在IO。
Bind是查询符号表,设置指向镜像外部的指针,性能消耗主要在CPU计算。
Objc setup
runtime会维护一张类名与类的方法列表的全局表。
- 读取所有类,将类对象其注册到这个全局表中(
class registration) - 读取所有分类,把分类加载到类对象中(
category registration) - 检查
selector的唯一性(selector uniquing)
initalizer time
这部分其实就是load方法的耗时
DYLD_PRINT_STATISTICS_DETAILS
还可以获取更详细的时间,添加环境变量DYLD_PRINT_STATISTICS_DETAILS,value设为YES。
优化思路
- 移除不需要用到的动态库,尽量使用系统库,且苹果建议数量控制在 6 个以下
- 移除不需要用到的类;合并功能类似的类和扩展;经测试 20000 个类会增加约 800毫秒
- 尽量进行懒加载,尽量避免在
load()方法里执行操作,把操作推迟到initialize()方法
继续了解优化请移步 iOS App启动优化(二):物理内存和虚拟内存