App启动速度优化

Wednesday, April 8, 2020

App启动速度优化

APP启动首先就分为冷启动和热启动,我们主要分析冷启动的速度优化。

启动过程

一般来讲,启动分为三个阶段。

  • main()函数执行之前
  • main()函数执行之后
  • 首屏渲染完成

appSpeed

图中已大概讲了一些优化方案和思路,总体而言就是在首屏渲染完成前,只处理首屏相关的业务,其他非首屏业务的初始化、监听注册、配置文件读取等都放到首屏渲染完成后去做。

另外根据相关经验,main 函数执行之前的阶段,优化空间并不大。所以主要还是在 main 函数开始执行,到首屏渲染结束前这个阶段去优化。

寻找优化空间

既然要优化了,那就得分析出哪些非必要方法比较耗时。那么该如何去监控方法运行时间呢?

Time Profiler

xcode 里面有个 Time Profiler 的工具,可以用来监测,大概原理是定时抓取主线程上的方法调用堆栈,计算一段时间内里各个方法的耗时。

但这个工具因为是定时抓取,那么这个间隔时间的设置就很成问题了。

  • 间隔时间过长,就会漏掉一些方法没检测到,导致不精确
  • 间隔时间过短,本身抓取堆栈的方法调用过多,影响整体耗时,导致不准确。

经验:如果设置0.002秒,是基本所有方法都能监测,但整体耗时不准确了。设置0.01秒,整体会较为准确,虽然会漏掉一些方法。大部分场景,这样设置去监测就够了。

使用 objc_msgSend 进行 hook 所有方法的耗时

关于 Runtime 本身,在以前就有详细记录,这里不再详细介绍。

OC 每个对象都会指向一个类,每个类都有一个方法列表,方法列表由 selector,函数指针,和 metadata 组成。

objc_msgSend 就通过对象和方法的selector 找到对应函数指针,然后执行。 所以 objc_msgSend 能控制 OC 的所有方法

objc_msgSend 由汇编编写,一来是这个调用频次非常高,所以需要极致性能,二来是其他语言也难以实现未知参数跳转到任意函数指针的功能

objc_msgSend 的 hook

这里较为复杂,不具体讲解,主要思路是找到调用方法的相应的指针,进行替换,自己写一个 pushCallRecord 函数和 PopCallRecord 函数,中间调用原始的 objc_msgSend,保存返回值。然后两个Record 函数记录的时间相减,即为原方法的调用时长。

iOS

双指针

链表习题