ios优化APP启动时间
我们进行优化的时候,我们将启动时间分为 pre-main 时间和 main 函数到第一个界面渲染完成时间这两个部分。因为 APP 的入口在 main 函数 ,在 main 函数之后我们的代码才会执行。
这里有两个阶段
1. pre-main阶段
1.1. 加载应用的可执行文件
1.2. 加载动态链接库加载器dyld(dynamic loader)
1.3. dyld递归加载应用所有依赖的dylib(dynamic library 动态链接库),包括iOS系统的以及APP依赖的第三方库。
其中苹果提供了内建的测量方法, Xcode 中Edit scheme -> Run -> Aguments将DYLD_PRINT_STATISTICS 设为 1,运行得下面的结果
获取到每个部分占用的时间
1.3.1分析每个dylib(大部分是iOS系统的),找到其Mach-O文件,打开并读取验证有效性,找到代码签名注册到内核,最后对dylib的每个segment调用mmap()。
优化思路是:尽量减少dylib的使用个数,谨慎使用第三方SDK。
1.3.2 rebase/bind
dylib加载完成之后,它们处于相互独立的状态,需要绑定起来。在dylib的加载过程中,系统为了安全考虑,引入了ASLR(Address Space Layout Randomization)技术和代码签名。由于ASLR的存在,镜像(Image,包括可执行文件、dylib和bundle)会在随机的地址上加载,和之前指针指向的地址(preferred_address)会有一个偏差(slide),dyld需要修正这个偏差,来指向正确的地址。Rebase在前,Bind在后,Rebase做的是将镜像读入内存,修正镜像内部的指针,性能消耗主要在IO。Bind做的是查询符号表,设置指向镜像外部的指针,性能消耗主要在CPU计算。
优化思路是:
减少OC类、selector、category的数量。
减少C++虚函数的数量。
使用Swift struct,减少符号的数量。
1.3.3 OC的runtime需要维护一张类名与类的方法列表的全局表。
dyld做了如下操作:
对所有声明过的OC类,将其注册到这个全局表中(class registration)
将category的方法插入到类的方法列表中(category registration)
检查每个selector的唯一性(selector uniquing)
initializer
这是pre-main阶段最耗时的部分。dyld运行APP的初始化函数,调用每个OC类的+load方法,调用C++的构造器函数(attribute((constructor))修饰),创建非基本类型的C++静态全局变量,然后执行main函数。
优化思路是
尽量避免在+load方法里执行的操作,可以推迟到+initialize方法中。
减少C++构造器函数个数。
减少C++静态全局变量的个数。
该阶段具体优化方法
a:尽量去掉不需要的第三方SDK,仅引用一些必需的重度依赖的SDK。
b:减少OC类,分类和唯一Selector的个数加快动态link,在进行动态库的重定位和绑定(Rebase/binding)过程中减少指针修正的使用,加快程序机器码的生成.尤其是Category和OC的动态绑定有关,可以合并。
c:减少OC初始化时间。类,分类的注册,唯一Selector,Category和OC的动态绑定有关,可以合并。以及,子父类内存布局的Non Fragile ivars偏移的更新,都会影响Objective-C运行时初始化的时间消耗.
d:如果MJExtension仅在解析一个广告的JSON数据时用到一次。删除该SDK,自己写代码解析即可。
e:load方法中的东西精简。可在initialize执行。
f:d无用文件及资源删除这个一般可以借助一些工具来实现。https://github.com/dblock/fui
图片资源:去除无用的图片; 适当进行图片压缩。