app启动分为冷启动和热启动两种。所谓的冷启动和热启动主要取决于该app进程是否在系统中。我们主要考虑的是冷启动的优化,因为这才是一个完整的启动过程,热启动中我们能干涉的其实很少
- main函数之前
可以通过添加添加环境变量 DYLD_PRINT_STATISTICS 来查看main函数执行之前都做了什么,同时也可以看出对应消耗的时间。- main函数执行之前主要做了以下几种事情
Total pre-main time: 17.00 milliseconds (100.0%)
dylib loading time: 140.89 milliseconds (828.5%)
rebase/binding time: 126687488.7 seconds (247154424.8%)
ObjC setup time: 16.27 milliseconds (95.7%)
initializer time: 57.75 milliseconds (339.6%)
slowest intializers :
libSystem.B.dylib : 3.70 milliseconds (21.7%)
libBacktraceRecording.dylib : 5.09 milliseconds (29.9%)
libobjc.A.dylib : 1.01 milliseconds (5.9%)
CoreFoundation : 1.24 milliseconds (7.3%)
Foundation : 0.35 milliseconds (2.1%)
libMainThreadChecker.dylib : 43.83 milliseconds (257.7%)
libLLVMContainer.dylib : 1.18 milliseconds (6.9%)
1、动态库的加载
对应的是dylib loading time可以看到其加载时间
优化建议
这里主要的优化建议是减少动态库的加载,苹果建议更少的使用动态库,如果动态库的数量较多的时候,尽量将多个动态库合并,数量上最多支持6个非系统动态库的合并-
2、偏移修正和符号绑定
对应的是rebase/binding time- 偏移修正
任何App生成的二进制文件中的方法、函数都会有个地址,而这个地址是相对于当前二进制文件中的偏移地址,但是到了运行时系统会随机生成一个数值添加到二进制文件的头部(ASLR安全机制下文中会有讲解),所以此时函数、方法的地址就是 随机分配的数值+偏移地址 这个过程就是偏移修正 - 符号绑定
动态库不像是静态库,静态库实在编译时期就将对应使用到的代码一起打包生成了mach-o文件,所以此时使用到的静态库的方法、函数其实就和自定义的方法、函数差不多了,能够直接获取到对应的地址,但是动态库在编译阶段是不会被打包进mach-o文件的,但是此时又用到了动态库中的方法,例如用到了NSLog方法,此时就会生成一个!NSLog 符号此时这个符号会随机指向一个地址,当运行时,此时动态库被加载到内存,此时就可以拿到动态库对应的方法、函数的地址,所以此时就需要将!NSLog这个符号绑定到相应的地址上去(dyld做的),这个过程就叫做符号绑定
- 偏移修正
3、类的注册
对应的是ObjC setup time
优化建议
删除启动后不会去使用的类4、执行load和构造函数
对应的是initializer time
优化建议
减少使用load方法相应的可以将load中的实现放在+initialize()方法中去,因为一般一个load方法的执行需要耗时4毫秒,而且如果类中实现了load那么相对应类的加载就要提前到read_image方法中去执行,如果没有实现load类的加载则会方法第一次发送消息的时候加载,-
main函数之后
这个阶段主要是指main函数执行开始到首屏渲染完成方法执行完毕。
这个阶段主要做的工作包括:1、第三方SDK初始化
2、自定义工具类初始化
3、首屏数据的加载
4、首屏渲染的一些计算
优化建议
只处理首屏渲染相关的任务,其他非首屏的业务例如初始化、注册监听、配置文件的读取等等都放在首页渲染完成之后去做,当然也可以开辟一个线程去处理这些事情。尽量不要占用主线程
自己的业务逻辑的优化,已经废弃的不需要用的逻辑代码、方法、函数都删除掉,减少每个流程的耗时
启动时期的页面尽量避免使用xib、storyboard(中间会有个转换的过程也是需要耗时的)UI的主框架尽量使用纯代码