App启动速度优化记录
前因
优化了 App 的启动速度,做个记录。
基础知识
App 的启动总时间 T = T1 + T2 + T3
T1:main函数之前
T2:main函数之后到 didFinishLaunchingWithOptions 方法执行完毕
T3:首页渲染
T1的主要工作有:
1 | exe() -> 加载可执行文件 -> 加载 dyld(the dynamic link editor)-> dyld 加载其他动态库 -> Rebase -> Bind -> Objc -> initializer |
操作系统首先为 App 进程分配一段内存空间,然后执行以下操作:
- 把 App 对应的可执行文件加载到内存
- 把 dyld 加载到内存
- dyld 进行动态链接
dyld 在各阶段所做的事情:
加载其他动态库
dyld 从主执行文件的 header 获取到需要加载的所依赖动态库列表,然后它需要找到每个 dylib,而应用所依赖的 dylib 文件可能会再依赖其他 dylib,所以需要加载的动态库列表是一个递归依赖的集合。
Rebase 和 Bind
Rebase 在 Image 内部调整指针的指向。在过去,会把动态库加载到指定地址,所有指针和数据对于代码都是对的。而现在地址空间是随机的,所有需要在原来的地址根据随意的偏移量做一下修正。
Bind 是把指针正确地指向 Image 外部的内容。这些指向外部的指针被符号(symbol)名称绑定,dyld 需要去符号表里查找,找到 symbol 对应的实现。
Objc setup
注册 Objc 类(class registration)
把 category 的定义插入方法列表 (category registration)
保证每一个 selector 唯一 (selector uniquing)Initializers
Objc 的 +load() 函数
C++ 的构造函数、属性函数
非基础类型的 C++ 静态全局变量的创建(通常是类和结构体)
最后 dyld 会调用 main() 函数,main() 会调用 UIApplicationMain(),befor main() 的过程也就此完成。
影响T1时间的因素:
- 动态库加载越多,启动越慢
- Objc 类,方法越多,启动越慢
- Objc 的 +load 越多,启动越慢
- C 的 constructor 函数越多,启动越慢
- C++ 动态对象越多,启动越慢
T2的主要工作有:
各类 SDK 初始化,H5 资源包下载,数据库配置等操作
影响T2时间的因素:
一些可以延后初始化的 SDK / 下载的操作全都堆在 didFinishLaunchingWithOptions 方法里
T3的主要工作有:
请求网络数据,渲染首页 UI
影响T3时间的因素:
网络请求,页面渲染
具体实践
T1
指定辅助 pod 库在 Debug 环境下运行
1
2pod 'MLeaksFinder', :configurations => ['Debug']
pod 'FLEX', :git => 'https://github.com/36Kr-Mobile/FLEX.git', :commit => '49cb9e9e6ee29f48f4ebbc4810817ef4ad1e208a', :configurations => ['Debug']去掉项目中过期类文件、方法、宏定义等
在 Edit Scheme —> Run —> Arguments —> Environment Vaiables 里添加 DYLD_PRINT_STATISTICS 为1可以看到 main 函数之前的执行时间
优化前:
优化后:
T2
把一些非必要的 SDK 放在首页渲染后再进行初始化
1 | // MARK: 延后注册的 SDK |
T3
把网络请求提前,放在开屏广告页请求