APP启动优化

App启动主要包括三个阶段

  1. main()函数执行前
  2. main()函数执行后
  3. 首屏渲染完成

main函数执行前

  • 加载可执行文件
  • 加载动态链接库
  • Objc运行时的初始处理,包括Objc相关类的注册、category注册、selector唯一性检查等
  • 初始化,包括执行+load()方法,attribute((constructor))修饰的函数调用、创建C++静态全局变量

因此可做的优化:

  • 减少动态库加载。每个库本身都有依赖关系,使用更少的动态库,如果使用的动态库数量较多,尽量将多个动态库合并。苹果支持做多可以支持6个非系统动态库合并为一个
  • 减少加载启动后不会去使用的类或者方法
  • +load()方法内容可以放到首屏渲染完成后再执行,或者使用+initialize()方法替换。因为,在一个+load()方法中,进行运行时方法替换操作会带来4好眠的消耗,避免积少成多,对启动速度带来影响
  • 控制C++全局变量的数量

main函数执行后

main()执行后的阶段,从main()函数执行开始,到appDelegatedidFinishLaunchingWithOptions方法里首屏渲染相关方法执行完成

这个阶段主要为首页需要的业务代码,包括:

  • 首屏初始化所需配置文件读写操作
  • 首屏列表大数据的读取
  • 首屏渲染的大量计算等

我们不应该把各种初始化都放在这个时候,应该只在这个时候放置首屏渲染必须的任务

首屏渲染完成后

非首屏其它业务模块的初始化,监听的注册、配置文件的读取等。即,截止到didFinishLaunchingWithOptions方法作用域执行收评渲染之后所有方法执行完成。

优化

功能级别优化

main()函数开始执行后,到首屏渲染完成前只处理首屏相关的业务,其他非首屏业务的初始化、监听注册、配置文件读取等都放到首屏渲染完成后去做

方法级别的优化

即检查首屏渲染完成前主线程有哪些耗时的方法,将耗时方法滞后或者异步。

常见的耗时方法通常是计算大量数据时,即加载、编辑、存储图片和文件等资源时,但是也有可能为其它方法类似load方法太多的堆积耗时

1. 定时抓取主线程上的方法堆栈,计算一段时间内各个方法的耗时

Xcode中的Time Profiler就是这种方式

这种方法需要考虑设置抓取的定时间隔,间隔长了会漏掉一些方法,导致检查出来耗时不准确;而设置的短了,抓取堆栈方法本身调用过多也会影响整体耗时,导致结果不准确

一般将时间设置为0.01s,这样对整体耗时影响小,虽然此时很多耗时方法不准确了,但是因为整体耗时更加重要,这也是够用的

2. 对obcc_msdSend方法进行hook掌握所有方法的执行耗时

hook objc_msgSend优点是非常精确,而缺点即只能针对OC方法,对C方法和block方法需要使用libffiffi_call来hook了,但是编写维护相关工具门槛较高

原理:
OC中的每个对象都会指向一个类,每个类也都有一个方法类表,方法列表中的方法由selector函数指针metadata组成,objc_msgSend就是在运行时根据对象和方法的selector去找相应的函数指针,然后执行,然后执行,因此objc_msgSend是OC放大执行的必经之路
objc_msgSend调用频率最高,本身是用汇编语言写的,可以提高app生命周期的性能,也可以实现未知参数跳转到任意函数指针的功能

Hook objc_msgSend方法

Fackbook开源了一个库,可以再iOS上运行的Mach-O二进制文件中动态地重新绑定符号,fishhook源码地址