App启动的三个阶段:
- main()函数执行前;
- main()函数执行后;
- 首屏渲染完成后。
main()函数执行前
- 加载可执行文件
- 加载动态链接库
- Objc运行时初始处理,相关类的注册、category注册、selector唯一性检查等
- 初始化,执行+load()方法、__attribute__((constructor))修饰的函数调用、创建c++静态全局变量
此阶段优化方案:
- 减少动态库加载 (可以将多个动态库合并,非系统动态库最多6个合为一个)
- 减少类、分类、方法(selector)的数量
- 用+initialize方法和dispatch_once取代所有的ObjC的+load
- 减少c++全局变量的数量
main()函数执行后
此阶段是指main()函数开始执行 -> didFinishLaunchingWithOptions里首屏渲染方法执行完成
注意首页业务代码是在首屏渲染完成前执行
- 首屏相关基础库初始化
- 首屏相关业务数据读取和处理
- 首屏渲染的大量计算
此阶段优化方案
- 各种初始化工作,不要都放到此阶段里面,会导致首屏渲染滞后。启动必要初始化,首屏渲染必要初始化,可以放在此阶段。其他的初始化放到对应功能使用之前。
首屏渲染完成后
此阶段是指 didFinishLaunchingWithOptions 方法作用域内首屏渲染之后的所有方法执行完成
非首屏其他业务服务模块的初始化
- 监听的注册
- 配置文件的读取等
- 此阶段首页信息已经显示完成
此阶段优化方案
- 主要优化主线程耗时方法,滞后执行或者异步执行,因为此阶段已经显示首页,主要优化用户的交互。
App启动速度的监控
通过在工程的scheme中添加环境变量DYLD_PRINT_STATISTICS,设置值为1,App启动加载时Xcode的控制台就会有pre-main各个阶段的详细耗时输出。
另外两种方法:
第一种方法:定时抓取主线程上的方法调用堆栈,计算一段时间里各个方法的耗时
Time Profiler 就是采用这种方式。
注意点
定时间隔长则会漏掉某些耗时短的方法
定时间隔短,抓取堆栈的方法本身会频繁调用,影响整体耗时
一般设置0.01秒,对整体耗时影响小,但是检测的很多方法耗时就不准确了。不过整体耗时数据比较重要,单个耗时不准可以接受。
如果设置0.002秒基本所有方法都可以检测,整体耗时就不准了。
第二种方法:对 objc_msgSend 方法进行hook来掌握所有方法的耗时