想做app启动优化,当然是先了解app启动,什么时候开始?什么时候结束?哪里是我们可以去优化的地方?
app启动开始:加载应用的可执行文件Mach-O
app启动结束:调用didFinishLaunchingWithOptions
- app启动这里我分成两个阶段:pre-main阶段和main()阶段
pre-main阶段
1.加载应用的可执行文件Mach-O
2.加载动态链接库加载器dyld(dynamic loader)
3.dyld递归加载应用所有依赖的dylib (dynamic library动态链接库)
main()阶段
1.dyld调用main()
2.调用UIApplicationMain()
3.调用applicationWillFinishLaunching
4.调用didFinishLaunchingWithOptions
好了,任务分好了,开始干活!!!
注:优化时间=优化前时间-优化后时间
//下面使用模拟器,建议使用真机+release+冷启动,得到数据才最真实
一.pre-main阶段
屏幕快照 2019-01-16 下午1.59.12.png在 Xcode 中 Edit scheme -> Run -> Arguments 将环境变量DYLD_PRINT_STATISTICS 设为1 ,打钩
屏幕快照 2019-01-16 下午2.05.25.png根据官方给的四个time,我们可以分为四个步骤
- 1.load dylibs
这一阶段dyld会分析应用依赖的dylib,找到其mach-o文件,打开和读取这些文件并验证其有效性,递归加载所有依赖库。
一般情况下,iOS应用会加载几百个dylibs,其中大部分是系统库,这部分对dylib的加载做了缓存操作。
所以,依赖的dylib越少越好,我们可以优化的地方:
1.尽可能减少依赖的dylib的数量
2.使用开销较小的dylib
- 2.rebase/bind
在dylib的加载过程中,系统为了安全考虑,引入了ASLR(Address Space Layout Randomization)技术和代码签名。由于ASLR的存在,镜像(Image,包括可执行文件、dylib和bundle)会在随机的地址上加载,和之前指针指向的地址(preferred_address)会有一个偏差(slide),dyld需要修正这个偏差,来指向正确的地址。
Rebase在前,Bind在后,Rebase做的是将镜像读入内存,修正镜像内部的指针,性能消耗主要在IO。Bind做的是查询符号表,设置指向镜像外部的指针,性能消耗主要在CPU计算。
所以,指针数量越少越好,我们可以做的优化有:
1.减少类Class、方法selector、分类category的数量
2.减少C++虚函数的的数量(创建虚函数表有开销)
3.使用Swift structs(内部做了优化,符号数量更少)
- 3.Objc setup
大部分ObjC初始化工作已经在Rebase/Bind阶段做完了,这一步dyld会注册所有声明过的ObjC类,将分类插入到类的方法列表里,再检查每个selector的唯一性。
在这一步倒没什么优化可做的,Rebase/Bind阶段优化好了,这一步的耗时也会减少。
- 4.Initializers
到了这一阶段,dyld开始运行程序的初始化函数,调用每个Objc类和分类的+load方法,调用C/C++ 中的构造器函数(用attribute((constructor))修饰的函数),和创建非基本类型的C++静态全局变量。Initializers阶段执行完后,dyld开始调用main()函数。
在这一步,我们可以做的优化有:
1.少在类的+load方法里做事情,尽量把这些事情推迟到+initiailize
2.减少构造器函数个数,在构造器函数里少做些事情
3.减少C++静态全局变量的个数
二.main()阶段
屏幕快照 2019-01-16 下午3.24.51.png 屏幕快照 2019-01-16 下午3.29.58.png在mian.m和AppDelegate.m中加上以上代码,可以获得main()之后启动时间
这个阶段牵扯到我们的业务,功能的实现
- 参考个人项目:在didFinishLaunchingWithOptions方法中
是否显示引导页、是否需要登录、新版本的检测、注册推送、网络状态的检测、三方库的配置和初始化等
首页控制器的加载,Tabbar和Nav控制器的加载等
根据个人经验做出一些优化建议:
这一阶段的优化主要是减少didFinishLaunchingWithOptions方法里的时间,所以我们要确认每个任务的时间量(利用CFAbsoluteTimeGetCurrent()方法去计算,计算方法参考上面),然后找出影响大的去做优化(当然时间容许全部优化最好)
1.满足业务前提下,didFinishLaunchingWithOptions在主线程里做的事情越少越好
-
将三方库和一些功能延迟执行,比如放在首页的viewDidAppear方法中
-
将三方库和一些功能异步执行,比如异步检测新版本的更新,注册推送
-
避免多余复杂的计算,比如合适的算法减少运算量和内存消耗
-
采用性能好的API,轻量级的对象
-
首页控制器用纯代码方式来构建
网友评论