App启动优化

冷启动定义

1、我们把用户点击App图标(App处于杀死状态)到首页渲染完这段时间定义为冷启动。
其中点击App图标->main函数定义为T1阶段。main函数-> AppDelegate didFinishLaunching这个阶段定义为T2阶段。AppDelegate didFinishLaunching->首页渲染完定义为T3阶段。

2、冷启动开始&结束时间节点
结束时间点:我们可以将首页某些视图元素的展示作为首页加载完成的标志。
开始时间点:一般情况下,我们都是在main()之后才开始接管App,但以main()函数作为冷启动起始点显然不合适,因为这样无法统计到T1时间段。那么,起始时间如何确定呢?目前业界常见的有两种方法,一是以可执行文件中任意一个类的+load方法的执行时间作为起始点;二是分析dylib的依赖关系,找到叶子节点的dylib,然后以其中某个类的+load方法的执行时间作为起始点。根据Dyld对dylib的加载顺序,后者的时机更早。但是这两种方法获取的起始点都只在Initializers阶段,而Initializers之前的时长都没有被计入。我们可以以App的进程创建时间(即exec函数执行时间)作为冷启动的起始时间。因为系统允许我们通过sysctl函数获得进程的有关信息,其中就包括进程创建的时间戳。

点击App图标->main函数 T1 阶段优化

过程分解


T1整体分为如下阶段:
1、加载可执行文件到内存中
调用exec系统方法创建进程,并且分配内存。
2、加载dyld到内存中
3、 dyld加载动态库
这个阶段做的事情如下表格所示

阶段 工作
加载动态库 dyld从主执行文件的header获取到需要加载的动态库列表,然后找到每个dylib,而应用所依赖的dylib文件可能会再依赖其他 dylib,所以所需要加载的是动态库列表一个递归依赖的集合
Rebase和Bind - Rebase在Image内部调整指针的指向。在过去,会把动态库加载到指定地址,所有指针和数据对于代码都是g固定的,而现在地址空间布局是随机化(ASLR),所以需要在原来的地址根据随机的偏移量做一下修正
- Bind是把指针正确地指向Image外部的内容。这些指向外部的指针被符号(symbol)名称绑定,dyld需要去符号表里查找,找到symbol对应的实现
Objc setup - 注册Objc类 (class registration)
- 把category的定义插入方法列表 (category registration)
- 保证每一个selector唯一 (selector uniquing)
Initializers - Objc的+load()函数
- C++的构造函数属性函数 attribute((constructor)) void DoSomeInitializationWork()
- 非基本类型的C++静态全局变量的创建(通常是类或结构体)

4、最后 dyld 会调用 main() 函数,main() 会调用 UIApplicationMain(),before main()的过程也就此完成。
了解完main()之前的加载过程后,我们可以分析出一些影响T1时间的因素:

  • 动态库加载越多,启动越慢。
  • ObjC类,方法越多,启动越慢。
  • ObjC的+load越多,启动越慢。
top Created with Sketch.