iOS 启动时间优化——相关

背景

iOS的启动时间优化有很多相关的文章,本来不打算写,但是总归是自己整理一遍印象更深刻。这里打算从原理-实践-面试相关三个方面来入手。首先要理解APP启动的原理,启动做了什么?然后针对启动过程中的事件哪些是开发可以进行优化的,去实践?最后则是,面试相关,与启动优化相关的面试有很多,笔者打算列举一下,分别涉及到启动的哪些问题。

启动原理

启动分为冷启动和热启动。冷启动指的是APP进程被杀掉后,从零打开;热启动指的是,APP进入后台,再切换到前台唤起到过程。这里通常说的启动优化,指的是冷启动优化。(Ps: 这里某些版本会出现,刚杀掉的APP,立即再次打开,会直接闪退的问题,是系统的Bug)。

冷启动的过程分为main函数之前(pre-main)main函数之后两个阶段。

pre-main阶段

就如很多文章说的:

pre-main阶段的过程分为dylibs loading ——> rebase/binding ——> ObjC setup ——> initializer四个部分。每个部分吧啦吧啦…但每次问到这个时候,死记硬背出来真的累人😂。

为什么会感觉困难呢?是因为不知道这四个步骤哪里来的,所以只能背,笔者记忆不好,终归是“不算年轻了”。
所以除了死记硬背,还能怎么办呢?去实践,然后理解,做过了的事情才印象深刻。

所以来看,如何实践&理解:

先打开Xcode的Scheme,然后在Run的选项下,选中Environment Variables,添加Name为DYLD_PRINT_STATISTICS,Value为1。如下:

wecom20210803-103910.png

然后运行一次,会发现控制台中打印出了类似如下的信息:

1
2
3
4
5
6
7
8
9
10
11

Total pre-main time: 599.52 milliseconds (100.0%)
dylib loading time: 101.25 milliseconds (16.8%)
rebase/binding time: 55.26 milliseconds (9.2%)
ObjC setup time: 189.95 milliseconds (31.6%)
initializer time: 253.04 milliseconds (42.2%)
slowest intializers :
libSystem.B.dylib : 6.65 milliseconds (1.1%)
libMainThreadChecker.dylib : 59.84 milliseconds (9.9%)
xxxTest : 313.12 milliseconds (52.2%)

这里面的信息仔细对比:其中Total pre-main time指的是pre-main的总时间;

Total pre-main timedylib loading time + rebase/binding time + ObjC setup time + initializer time;

slowest intializers中的libMainThreadChecker,是因为Scheme中的Main Thread Checker
打开了,关闭即可,如下的地方勾选去除即可。

wecom20210809-101508.png

所以pre-main阶段发生了什么?就是发生了dylib loadingrebase/bindingObjC setup timeinitializer time。这样就知道了这4个步骤的来历,接下来再看看这几个步骤都是做什么的?

参考:https://devstreaming-cdn.apple.com/videos/wwdc/2016/406i3zbazbegkeh0udt/406/406_optimizing_app_startup_time.pdf?dl=1

wecom20210809-092908@2x.png

wecom20210809-092815@2x.png

wecom20210809-092553@2x.png

wecom20210809-093129@2x.png

wecom20210809-093951@2x.png

翻译如下:

  • Total pre-main time指的是pre-main的总时间
    • 加载所有依赖的动态库;
    • 修正DATA中的指针偏差;
    • 初始化所有对象。
  • dylib loading time
    • 加载app依赖的库
    • 加载库依赖的库
  • rebase/binding time
    • rebasing: 调整镜像的指针
    • binding: 设置指针到外部镜像
  • ObjC setup time
    • 注册objc类(class registration);
    • Category方法插入;
    • 保证每个Selector唯一;
  • initializer time
    • C++静态对象初始化
    • objc的+load方法加载
    • 执行main()

      main函数之后阶段

main函数之后的优化,也要多注意,大家都知道application:didFinishLaunchingWithOptions:是启动时初始化的方法,那这里应该做些什么?

首先问大家一个问题

启动优化实践

application:didFinishLaunchingWithOptions:return YES之前写一个sleep(10);所以只要这个方法不返回,界面就会显示LaunchScreen,即使已经初始化了首页设置了rootVC也不会显示。

面试相关

+initialize、+init、+load什么时候调用、调用顺序?

+initialize、+load的区别?

+load的加载是在main函数之前?还是之后?

参考

Reducing Your App’s Launch Time
optimizing_app_startup_time
iOS深思篇 | 启动时间的度量和优化