启动
启动的过程一般是指从用户点击app图标开始到AppDelegate 的didFinishLaunching方法执行完成
为止,其中,启动也分为冷启动
和热启动
冷启动
:第一次打开app或app被杀死后重新打开叫冷启动(走didFinishLaunchWithOptions方法)热启动
:app在后台且存活的状态下,再次打开app叫热启动(不走didFinishLaunchWithOptions方法)
而我们这里所说的启动优化,一般是指冷启动情况下的,这种情况下的启动主要分为两部分:
T1
:pre-main阶段,即main函数之前,操作系统加载App可执行文件到内存,执行一系列的加载&链接等工作,简单来说,就是dyld加载过程T2
:main函数之后,即从main函数开始,到Appdelegate 的didFinishLaunching方法执行完成为止,主要是构建第一个界面,并完成渲染
所以,T1+T2
的过程 就是 从用户点击App图标到用户能看到app主界面的过程,即需要启动优化的部分
pre-main阶段的优化
通过Edit scheme
--> Run
--> Arguments
添加环境变量DYLD_PRINT_STATISTICS
,值设为1
运行程序,打印出如下数据,这些属于main函数之前的时间,也就是pre-main
Total pre-main time: 726.83 milliseconds (100.0%)
dylib loading time: 39.36 milliseconds (5.4%)
rebase/binding time: 50.96 milliseconds (7.0%)
ObjC setup time: 15.55 milliseconds (2.1%)
initializer time: 620.94 milliseconds (85.4%)
slowest intializers :
libSystem.B.dylib : 6.22 milliseconds (0.8%)
libMainThreadChecker.dylib : 34.21 milliseconds (4.7%)
libglInterpose.dylib : 416.45 milliseconds (57.2%)
Ebusbar : 291.12 milliseconds (40.0%)
Total pre-main time
整个pre-main
花费的时间
dylib loading time
动态库的加载时间
优化方案:
我们主要是对自己调用的第三方动态库优化,苹果建议自定义的动态库不要超过6个,我们尽量减少使用自定义的动态库或者合并动态库
rebase/binding time
rebase
:偏移修正
任何一个app生成的二进制文件,在二进制文件内部所有的方法、函数调用,都有一个地址,这个地址是在当前二进制文件中的偏移地址。一旦在运行时刻(即运行到内存中),每次系统都会随机分配一个ASLR
(Address Space Layout Randomization,地址空间布局随机化
)地址值(是一个安全机制,会分配一个随机的数值,插入在二进制文件的开头),例如,二进制文件中有一个 test方法,偏移值是0x0001,而随机分配的ASLR是0x1f00,如果想访问test方法,其内存地址(即真实地址)变为 ASLR+偏移值 = 运行时确定的内存地址(即0x1f00+0x0001 = 0x1f01)binding
:绑定
例如NSLog
方法,在编译时期生成的mach-o文件中,会创建一个符号!NSLog
(目前指向一个随机的地址),然后在运行时(从磁盘加载到内存中,是一个镜像文件),会将真正的地址给符号
(即在内存中将地址与符号进行绑定,是dyld做的,也称为动态库符号绑定),一句话概括:绑定就是给符号赋值的过程
ObjC setup time
这一步做的主要操作就是类的加载,分类的合并,类越多越耗时
优化方案
:合并或者删减一些OC类,关于清理项目中没用到的类,使用工具AppCode
代码检查功能,查到当前项目中没有用到的类如下,清除一些没用的静态变量,清除一些没有用到的方法
initializer time
执行load和构造函数的耗时
优化方案
:将不必须在+load
方法中做的事情延迟到+initialize
中,尽量不要用C++虚函数
main函数阶段的优化
在main函数之后的didFinishLaunching
方法中,主要是执行了各种业务,有很多并不是必须在这里立即执行的,这种业务我们可以采取延迟加载
,防止影响启动时间
main函数阶段的优化建议主要有以下几点:
减少启动初始化的流程
,能懒加载的懒加载,能延迟的延迟,能放后台初始化的放后台,尽量不要占用主线程的启动时间优化代码逻辑,
去除非必须的代码逻辑
,减少每个流程的消耗时间启动阶段能使用
多线程
来初始化的,就使用多线程尽量使用
纯代码
来进行UI框架的搭建,尤其是主UI框架,例如UITabBarController。尽量避免使用Xib或者SB,相比纯代码而言,这种更耗时