APP启动优化
App启动主要包括三个阶段
- main()函数执行前
- main()函数执行后
- 首屏渲染完成
main函数执行前
- 加载可执行文件
- 加载动态链接库
- Objc运行时的初始处理,包括Objc相关类的注册、category注册、selector唯一性检查等
- 初始化,包括执行+load()方法,attribute((constructor))修饰的函数调用、创建C++静态全局变量
因此可做的优化:
- 减少动态库加载。每个库本身都有依赖关系,使用更少的动态库,如果使用的动态库数量较多,尽量将多个动态库合并。苹果支持做多可以支持6个非系统动态库合并为一个
- 减少加载启动后不会去使用的类或者方法
- +load()方法内容可以放到首屏渲染完成后再执行,或者使用
+initialize()
方法替换。因为,在一个+load()
方法中,进行运行时方法替换操作会带来4好眠的消耗,避免积少成多,对启动速度带来影响 - 控制C++全局变量的数量
main函数执行后
main()执行后的阶段,从main()函数执行开始,到appDelegate
中didFinishLaunchingWithOptions
方法里首屏渲染相关方法执行完成
这个阶段主要为首页需要的业务代码,包括:
- 首屏初始化所需配置文件读写操作
- 首屏列表大数据的读取
- 首屏渲染的大量计算等
我们不应该把各种初始化都放在这个时候,应该只在这个时候放置首屏渲染必须的任务
首屏渲染完成后
非首屏其它业务模块的初始化,监听的注册、配置文件的读取等。即,截止到didFinishLaunchingWithOptions
方法作用域执行收评渲染之后所有方法执行完成。
优化
功能级别优化
main()函数开始执行后,到首屏渲染完成前只处理首屏相关的业务,其他非首屏业务的初始化、监听注册、配置文件读取等都放到首屏渲染完成后去做
方法级别的优化
即检查首屏渲染完成前主线程有哪些耗时的方法,将耗时方法滞后或者异步。
常见的耗时方法通常是计算大量数据时,即加载、编辑、存储图片和文件等资源时,但是也有可能为其它方法类似load方法太多的堆积耗时
1. 定时抓取主线程上的方法堆栈,计算一段时间内各个方法的耗时
Xcode中的Time Profiler就是这种方式
这种方法需要考虑设置抓取的定时间隔,间隔长了会漏掉一些方法,导致检查出来耗时不准确;而设置的短了,抓取堆栈方法本身调用过多也会影响整体耗时,导致结果不准确
一般将时间设置为0.01s,这样对整体耗时影响小,虽然此时很多耗时方法不准确了,但是因为整体耗时更加重要,这也是够用的
2. 对obcc_msdSend方法进行hook掌握所有方法的执行耗时
hook objc_msgSend
优点是非常精确,而缺点即只能针对OC方法,对C方法和block方法需要使用libffi
的ffi_call
来hook了,但是编写维护相关工具门槛较高
原理:
OC中的每个对象都会指向一个类,每个类也都有一个方法类表,方法列表中的方法由selector
函数指针metadata
组成,objc_msgSend就是在运行时根据对象和方法的selector去找相应的函数指针,然后执行,然后执行,因此objc_msgSend是OC放大执行的必经之路
objc_msgSend调用频率最高,本身是用汇编语言写的,可以提高app生命周期的性能,也可以实现未知参数跳转到任意函数指针的功能
Hook objc_msgSend方法
Fackbook开源了一个库,可以再iOS上运行的Mach-O二进制文件中动态地重新绑定符号,fishhook源码地址