冷启动优化
更多的是业务层面的优化
发挥 cpu的价值,多开线程异步,让cpu达到最大价值
启动过程
Main 之前 —影响最大
(dyld 监控)耗时
动态库的加载 不超过4-6个
Main之后 基本上是业务层面
Total pre-main time: 704.32 milliseconds (100.0%) // 总共启动时间
dylib loading time: 174.75 milliseconds (24.8%) // 动态库加载时间
rebase/binding time: 36.61 milliseconds (5.1%) // 修复内部指针地址(ASLR 随机偏移值)/外部符号绑定(DYLD去做的)
ObjC setup time: 170.39 milliseconds (24.1%) // OC类注册的耗时时间
initializer time: 322.29 milliseconds (45.7%) // load 的时间
slowest intializers : // 启动最耗时的内容
libSystem.B.dylib : 10.14 milliseconds (1.4%) // 系统库
libMainThreadChecker.dylib : 85.19 milliseconds (12.0%) //系统库
(App名称) : 411.80 milliseconds (58.4%) // 主程序
在Main函数之前,有哪些是可以 做 优化的:
1、库的加载
系统库经过优化处理的,本身加载就 很快。
自己倒入的库: 苹果给出的建议是不超过6个。如果超过6个,可以采用合并的方式。
2、减少不必要的类 、资源图片等。
比如随着版本更新迭代,有些类、图片等被弃用了的。
可以使用工具检测没有用到的类
这个操作相对来说优化的成效不高。有人说 减少2w个类,启动时间只少了800 毫秒。
3、能不在Load里面做的操作,就不要在Load操作。
4、二进制重排
这个主要是正对binding阶段的操作。参考抖音ios团队的经验
基于静态扫描+运行时trace的方案仍然存在少量瓶颈:
initialize hook不到
部分block hook不到
C++通过寄存器的间接函数调用静态扫描不出来
目前的重排方案能够覆盖到80%~90%的符号,未来我们会尝试编译期插桩等方案来进行100%的符号覆盖,让重排达到最优效果。
Main 之后的优化
1、能懒加载的就懒加载
2、发货CPU的性能(多线程初始化)
3、启动阶段的尽量不要用Xib、stroyboard。 Xib、storyboard都是需要进行xml解析,相对纯代码来讲,是比较耗时的。
2016 中首次出现了 App 启动优化的话题,其中提到:
App 启动最佳速度是400ms以内,因为从点击 App 图标启动,然后 Launch Screen 出现再消失的时间就是400ms;
App 启动最慢不得大于20s,否则进程会被系统杀死;(启动时间最好以 App 所支持的最低配置设备为准。)
指针重定位优化方案:
减少 ObjC 类(class)、方法(selector)、分类(category)的数量,比如合并一些功能,删除无效的类、方法和分类等(可以借助AppCode的 Inspect Code 功能进行代码瘦身);
减少 C++ 虚函数;(虚函数会创建 vtable,这也会在 __DATA 段中创建结构。)
多用 Swift Structs。(因为 Swift Structs 是静态分发的,它的结构内部做了优化,符号数量更少。)
Initializers
优化方案
尽量避免在类的 +load 方法中初始化,可以推迟到 +initiailize 中进行;(因为在一个 +load 方法中进行运行时方法替换操作会带来 4ms 的消耗)
避免使用 __atribute__((constructor)) 将方法显式标记为初始化器,而是让初始化方法调用时再执行。比如用 dispatch_once()、pthread_once() 或 std::once(),相当于在第一次使用时才初始化,推迟了一部分工作耗时。:
减少非基本类型的 C++ 静态全局变量的个数。(因为这类全局变量通常是类或者结构体,如果在构造函数中有繁重的工作,就会拖慢启动速度
总结一下 pre-main 阶段可行的优化方案:
重新梳理架构,减少不必要的内置动态库数量
进行代码瘦身,合并或删除无效的ObjC类、Category、方法、C++ 静态全局变量等
将不必须在 +load 方法中执行的任务延迟到 +initialize 中
减少 C++ 虚函数
main 之后
启动优化
main() 被调用之后,didFinishLaunchingWithOptions 阶段,App 会进行必要的初始化操作,而 viewDidAppear 执行结束之前则是做了首页内容的加载和显示。
关于App 的初始化,除了统计、日志这种须要在 App 一启动就配置的事件,有一些配置也可以考虑延迟加载。如果你在 didFinishLaunchingWithOptions 中同时也涉及到了首屏的加载,那么可以考虑从这些角度优化:
用纯代码的方式,而不是 xib/Storyboard,来加载首页视图
延迟暂时不需要的二方/三方库加载;
延迟执行部分业务逻辑和 UI 配置;
延迟加载/懒加载部分视图;
避免首屏加载时大量的本地/网络数据读取;
在 release 包中移除 NSLog 打印;
在视觉可接受的范围内,压缩页面中的图片大小;