一、启动流程
1.1、pre-main阶段
1、加载应用可执行文件
自身App的所有.o文件的集合
2、加载dyld动态链接器
dyld是一个专门用来加载动态链接库的库
3、dyld递归加载应用所有依赖的动态链接库dylib
动态链接库包括:iOS 中用到的所有系统 framework,加载OC runtime方法的libobjc,系统级别的libSystem,例如libdispatch(GCD)和libsystem_blocks (Block)。
1.2、main()阶段
- dyld调用main()
- 调用UIApplicationMain()
- 调用applicationWillFinishLaunching
- 调用didFinishLaunchingWithOptions
1.3、首屏渲染
首屏构建完成可浏览/可操作页面
二、查看启动时长
ios15之前,Xcode菜单Product-Scheme-edit Scheme...0-Run-Arguments-Environment Variables,添加 DYLD_PRINT_STATISTICS
环境变量,value为1,可以看到打印输出pre-main()之前的启动各项时长
Total pre-main time: 539.46 milliseconds (100.0%)//main()总的启动时间
dylib loading time: 232.89 milliseconds (43.1%)//加载动态库占用
rebase/binding time: 32.56 milliseconds (6.0%)//指针重定位占用
ObjC setup time: 22.18 milliseconds (4.1%)//ObjC类初始化占用
initializer time: 251.68 milliseconds (46.6%)//各种初始化占用
slowest intializers :
libSystem.B.dylib : 5.12 milliseconds (0.9%)//intializers初始化最耗时1
libMainThreadChecker.dylib : 29.74 milliseconds (5.5%)//intializers初始化最耗时2
Template : 382.83 milliseconds (70.9%)//intializers初始化最耗时3
三、优化
pre-main
- dylib loading: 加载动态库。这里的动态库指的是自己制作的动态库存,苹果提供的动态库存是在共享缓存里的,可以通过减少动态库的数量来优化这一部分所消耗的时间。苹果的建议是一个项目里自己制作的动态库数量不超过6个。
- Objc setup: 注册Ojbc类,进行selector唯一性检测等。可以通过减少Ojbc类的数量,减少selector的数量来进行优化
- rebase/binding: rebase指针修复,binding符号绑定。这一步优化手段和第2步一样。
- initializer: 各种初始化的操作,比如执行objc的+load函数,C++的构造函数等,不要在+load函数里做一些耗时操作,或者把一引起操作延时的放在+initializer里面去执行
- slowest intializers:在共享缓存里加载,都是苹果系统的动态库,经过了苹果的高效优化
main
-
减少使用xib和storyboard
-
删除NSLog打印,每次用NSLog方式打印会隐式的创建一个Calendar,因此需要删减启动时各业务方打的log,或者仅仅针对内测版输出log
-
整理didFinishLaunchingWithOptions⽅法⾥⾯的业务逻辑,可以异步请求的异步请求,可
以延时加载的延时加载。
四、二进制重排
ASLR
ASLR(address space layout randomization),地址空间布局随机化。是⼀种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测⽬的地址的难度,防⽌攻击者直接定位攻击代码位置,达到阻⽌溢出攻击的⽬的的⼀种技术。使得可执⾏⽂件和动态库在虚拟内存中的地址在每次启动都不固定。
Page Fault
由于虚拟内存的出现,进程不直接访问物理内存,这样安全性更⾼。为了提⾼效率和⽅便管理,对虚拟内存和物理内存进⾏分⻚(Page)管理。当进程访问⼀个虚拟内存⻚,⽽对应的物理内存却不存在的时候,会触发⼀个缺⻚中断,这个就叫Page Fault。
二进制重排
概念
:找到程序在启动时候需要调⽤的符号,然后修改编译参数完成⼆进制⽂件的重新排布。
LinkMap
:是iOS编译过程的中间产物,记录了⼆进制⽂件的布局。可以在Xcode的Build Settings⾥开启Write Link Map File。
order⽂件
:编译器会按照order⽂件的内容,对⼆进制⽂件进⾏排列。可以在Xcode的BuildSettings⾥的Order File处设置。
⼆进制重排能够优化启动时间的原理
:App在执⾏的过程中会存在⼤量的Page Fault,⼀个Page Fault的耗时很少,但是当⼤量的Page Fault存在时,就会影响到代码的执⾏速度。同理,在App启动的时候,就可能会出现⼤量的Page Fault。⼆进制重排就是把在启动过程中需要使⽤到的符号,重新排列在⼀个或者⼏个Page⾥⾯,减少Page Fault的次数,从⽽达到减少启动时间的⽬的。
五、优化手段总结
- 移除不必要的动态库和不必要的类,使用fui(Find Unused Imports)可以扫描没有使用到的类
- 二进制重排优化
- 合并功能相似的类和扩展,由于Category的实现原理,和ObjC的动态绑定有很强的关系,实际上类的扩展是比较占用启动时间的。尽量可能合并一些扩展,并不是让你不使用扩展
- 压缩资源图片,图片小了,IO操作量小了,启动就快了
- 优化applicationWillFinishLaunching,需要在
applicationWillFinishLaunching
里处理的业务较多时,可以管理起这些任务将不需要马上在applicationWillFinishLaunching执行的代码延后执行 - 不使用xib,直接视用代码加载首页视图
- NSUserDefaults实际上是在Library文件夹下会生产一个plist文件,如果文件太大的话一次能读取到内存中可能很耗时
- 删减启动时各业务方打的log