一、App启动都做了哪些事情? iOS启动分为热启动和冷启动两种
- 冷启动: App点击前, 不在进程系统中, 需要创建一个进程给他启动, 这是一次完整的启动过程
- 热启动: App在冷启动后, 退到后台, 此时App的进程还在系统中, 重新启动App的过程, 做的事情非常少
二、App的启动主要包括三个阶段
- main()函数执行之前(Pre-main阶段)
- dylib loading: 加载可执行文件
- rebase/binding: rebase指针调整和bind符号绑定
- Objc setup: Objc runtime初始化, Class注册, Category注册, Selector检测等
- initializer: 初始化, +load()方法执行, C++全局变量初始化等
- main()函数执行后(main执行到设置rootViewController前)
- TabbarController的加载
- 首页Controller的加载, 以及其子Controller的加载
- 首屏渲染完成后(设置rootViewController后到didFinishLaunchWithOptions)
- 初始化一些其他功能(第三方库等)
三、启动时间测量
-
录屏工具: 对App启动过程录制, 使用视频剪辑工具解帧计算. 这样可以最直观的看到, App从点击图标, 到首页出现的总时间,但是也只是从感官体验到App打开很快或很慢, 缺少具体的更有力的证据
-
Pre-main: 在 Scheme -> Environment Variables 中添加DYLD_PRINT_STATISTICS, 值为YES, 控制台就会打印出Pre-Main time. 以及对应Pre-main阶段所做的事情
-
Instruments: 使用 Instruments 的 App Launch 工具我们可以查看到Pre-main之后的启动耗时
- 切换到 App Life Cycle 类型, 可以看到App每个阶段整体的耗时, 如图:
- 切换到 Profile 类型, 可以看到App各个线程/符号(函数)的耗时, 如图:
- 使用hook objc_msgSend, 具体实现大家可以参考戴铭大佬的文章.原文链接
四、怎么优化?
- main()函数执行之前(pre-main阶段)
- dylib loading:
- rebase/binding: 减少或合并动态库, 将动态库换成静态库
- Objc setup: 减少无效类和方法的使用, 合并Category
- initializer: 减少+load()方法的重写, 可以放到 +initialize()中
- main()函数执行后(main执行到设置rootViewController前)
- 减少首页viewDidload方法的方法
- 首页尽量少的递归创建VC
- 首屏渲染完成后(设置rootViewController后到didFinishLaunchWithOptions)
- 将第三方库, 版本控制等方法滞后处理