App的启动可以分为2种
- 1.冷启动(
Cold Launch
):从零开始启动App - 2.热启动(
Warm Launch
): App已经在内存,在后台存活着,再次点击图标启动App
App启动时间优化,主要针对冷启动进行优化
1.通过添加环境变量可以打印出App的启动时间分析Edit scheme -> Run -> Arguments
DYLD_PRINT_STATISTICS
设置为1
第1次运行Xcode打印时间
// 总共需要时间
Total pre-main time: 760.53 milliseconds (100.0%)
// 动态库加载时间
dylib loading time: 100.44 milliseconds (13.2%)
rebase/binding time: 496.51 milliseconds (65.2%)
// ObjC结构体准备
ObjC setup time: 119.91 milliseconds (15.7%)
// 初始化
initializer time: 43.54 milliseconds (5.7%)
// 比较慢的加载(比较耗时的)
slowest intializers :
// 动态库
libSystem.B.dylib : 8.72 milliseconds (1.1%)
libMainThreadChecker.dylib : 23.59 milliseconds (3.1%)
- dyld :Dynamic Link Editor, Apple的动态链接器,可以用来装载Mach-O文件(可执行文件,动态库)
启动App时,dyld所做的事情有:
- 装载App的的可执行文件,同时会递归加载所有依赖的动态库
- 当dyld把所有可执行文件,动态库装载完毕后,会通知runtime进行下一步的处理
启动App时,runtime所做的事情有:
- 调用map_images进行可执行文件内容的解析和处理
- 在load_images中调用了call_load_methods,调用所有的Class和Category的+load方法
- 进行各种ObjC结构的初始化(注册ObjC类,初始化类对象等待)
- 调用C++静态初始化器和__attribute__(consructor)修饰的函数
到此为止,可执行文件和动态库中所有的符号(Class,Protocol,Selector, IMP,…)都已经按格式成功加载到内存中,被runtime所管理
总结一下: 1.App的启动有dyld主导,将可执行文件加载到内存,加载所有依赖的动态库 2.由runtime负责加载成Objective-C定义的结构
所有初始化工作结束后,dyld就会调用main函数
接下来就是UIApplicationMain函数,AppDegate的application: didFinishLaunchingWithOptions:方法
第2次运行Xcode打印时间
dylib loading time: 72.89 milliseconds (37.5%)
rebase/binding time: 34.46 milliseconds (17.7%)
ObjC setup time: 55.50 milliseconds (28.5%)
initializer time: 31.31 milliseconds (16.1%)
slowest intializers :
libSystem.B.dylib : 4.24 milliseconds (2.1%)
libMainThreadChecker.dylib : 18.00 milliseconds (9.2%)
第3次运行Xcode打印时间
Total pre-main time: 304.79 milliseconds (100.0%)
dylib loading time: 182.09 milliseconds (59.7%)
rebase/binding time: 40.21 milliseconds (13.1%)
ObjC setup time: 52.77 milliseconds (17.3%)
initializer time: 29.52 milliseconds (9.6%)
slowest intializers :
libSystem.B.dylib : 2.71 milliseconds (0.8%)
libMainThreadChecker.dylib : 17.79 milliseconds (5.8%)
可以看到每次运行,这个时间并不准确,只能说给我们参考
需要更详细的信息,DYLD_PRINT_STATISTICS_DETAILS
设置为1
total time: 635.54 milliseconds (100.0%)
// 镜像加载
total images loaded: 257 (0 from dyld shared cache)
// 模块映射
total segments mapped: 764, into 103180 pages with 7208 pages pre-fetched
total images loading time: 453.85 milliseconds (71.4%)
total load time in ObjC: 57.09 milliseconds (8.9%)
total debugger pause time: 359.81 milliseconds (56.6%)
total dtrace DOF registration time: 0.17 milliseconds (0.0%)
total rebase fixups: 2,513,282
total rebase fixups time: 63.67 milliseconds (10.0%)
total binding fixups: 282,663
total binding fixups time: 31.07 milliseconds (4.8%)
total weak binding fixups time: 0.48 milliseconds (0.0%)
total redo shared cached bindings time: 37.20 milliseconds (5.8%)
total bindings lazily fixed up: 0 of 0
total time in initializers and ObjC +load: 29.17 milliseconds (4.5%)
libSystem.B.dylib : 2.60 milliseconds (0.4%)
libBacktraceRecording.dylib : 2.69 milliseconds (0.4%)
CoreFoundation : 1.64 milliseconds (0.2%)
Foundation : 2.04 milliseconds (0.3%)
libMainThreadChecker.dylib : 18.14 milliseconds (2.8%)
libLLVMContainer.dylib : 0.74 milliseconds (0.1%)
total symbol trie searches: 132646
total symbol table binary searches: 0
total images defining weak symbols: 20
total images using weak symbols: 61
App的冷启动可以概括为3大阶段
- 1.dyld 动态库
- 2.runtime 图片加载,本地资源包加载
- 3.main
归根到底可以总结为main之前,和main之后
App的冷启动优化
按照不同的阶段
dyld
- 减少动态库,合并一些动态库(定期清理不必要的动态库)
- 减少ObjC类,分类的数量,减少Selector数量(定期清理不必要的类,分类)
- 减少C++虚函数数量(会有虚表生成)
- Swift尽量使用struct
runtime
- 用
+initialize
方法和dispatch_once
取代所有的__attribute__((constructor))
,C++静态构造器,ObjC的+load
方法
main
在不影响用户体验的前提下,尽可能将一些操作延迟,不要全部都放在finishLaunching方法中,按需加载
当我们点击了 build 之后,做了什么事情呢?
- 1.预处理(Pre-process):把宏替换,删除注释,展开头文件,产生 .i 文件。
- 2.编译(Compliling):把之前的 .i 文件转换成汇编语言,产生 .s文件。
- 3.汇编(Asembly):把汇编语言文件转换为机器码文件,产生 .o 文件。
- 4.链接(Link):对.o文件中的对于其他的库的引用的地方进行引用,生成最后的可执行文件(同时也包括多个 .o 文件进行 link)