概要
App启动时长是其性能的重要指标之一,直接影响着用户对App的第一印象。随着版本迭代,产品形态不断完善,业务功能日趋复杂,项目也从一个独立业务App逐渐演变成一个多模块项目,甚至成为一个平台型App,可能同时承载多个业务模块,因此,更多更复杂的工作需要在App启动的时候被完成,由此可见,启动速度的优化必然就是 App 开发过程中,不可或缺的一个环节。
一般情况下,App 的启动分为冷启动和热启动。
- 冷启动:App 不在系统进程中,比如设备重启后,或是手动杀死 App 进程,又或是 App 长时间未打开过,用户再点击启动 App 的过程,这时需要创建一个新进程分配给 App。我们可以将冷启动看作一次完整的 App 启动过程。
- 热启动:当用户按下 home 键,App 不会立刻被 kill,而是存活一段时间,这段时间里用户再打开 App,App 基本上不需要做什么,就能还原到退到后台前的状态。我们把 App 进程还在系统中,无需开启新进程的启动过程称为热启动。
名称 | 区别 |
冷启动 | 启动时,App的进程不在系统里,需要开启新进程。 |
热启动 | 启动时,App的进程还在系统里,不需要开启新进程。 |
WWDC 2016 中首次出现了 App 启动优化的话题,其中提到:
- App 启动最佳速度是400ms以内,因为从点击 App 图标启动,然后 Launch Screen 出现再消失的时间就是400ms;
- App 启动最慢不得大于20s,否则进程会被系统杀死;(启动时间最好以 App 所支持的最低配置设备为准。)
所以,这篇文章,重点了解App 冷启动的优化。
App启动相关优化
冷启动过程
一般而言,把iOS冷启动的过程定义为:从用户点击App图标开始到appDelegate didFinishLaunching方法执行完成,最后到用户可以使用。这个过程主要分为三个阶段:
- T1:main()函数之前,即操作系统加载App可执行文件到内存,然后执行一系列的加载&链接等工作,最后执行至App的main()函数。
- T2:main()函数之后,即从main()开始,到appDelegate的didFinishLaunchingWithOptions方法执行完毕。
- T3:didFinishLaunchingWithOptions执行完成时,还需要做一些初始化工作,然后经历定位、首页请求、首页渲染等过程后,用户才能真正看到数据内容并开始使用。
综上,冷启动过程为:从用户点击App图标开始到用户能看到App主界面内容为止这个过程,即T1+T2+T3。在App冷启动过程当中,这三个阶段中的每个阶段都存在很多可以被优化的点。
1、优化T1
T1是调用main()函数之前阶段,基本所有的工作都是由操作系统完成的,开发者能够插手的地方不多,所以如果想要优化这段时间,就必须先了解一下,操作系统在main()之前做了什么:
App启动后,首先,系统内核(Kernel)创建一个进程。其次,加载可执行文件。(可执行文件是指Mach-O格式的文件,也就是App中所有.o文件的集合体)这时,能获取到dyld(dyld是苹果的动态链接器)的路径。
Mach-O
Mach-O(Mach Object File Format)是一种用于记录可执行文件、对象代码、共享库、动态加载代码和内存转储的文件格式。App 编译生成的二进制可执行文件就是 Mach-O 格式的,iOS 工程所有的类编译后会生成对应的目标文件 .o
文件,而这个可执行文件就是这些 .o
文件的集合。
在 Xcode 的控制台输入以下命令,可以打印出运行时所有加载进应用程序的 Mach-O 文件。
image list -o -f
Mach-O 文件主要由三部分组成:
- Mach header:描述 Mach-O 的 CPU 架构、文件类型以及加载命令等;
- Load commands:描述了文件中数据的具体组织结构,不同的数据类型使用不同的加载命令;
- Data:Data 中的每个段(segment)的数据都保存在这里,每个段都有一个或多个 Section,它们存放了具体的数据与代码,主要包含这三种类型:
__TEXT
包含 Mach header,被执行的代码和只读常量(如C 字符串)。只读可执行(r-x)。__DATA
包含全局变量,静态变量等。可读写(rw-)。__LINKEDIT
包含了加载程序的元数据,比如函数的名称和地址。只读(r–-)。