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
。如下:
然后运行一次,会发现控制台中打印出了类似如下的信息:
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 time
≈ dylib loading time
+ rebase/binding time
+ ObjC setup time
+ initializer time
;
slowest intializers
中的libMainThreadChecker
,是因为 Scheme 中的Main Thread Checker
打开了,关闭即可,如下的地方勾选去除即可。
所以pre-main
阶段发生了什么?就是发生了dylib loading
、rebase/binding
、ObjC setup time
、initializer time
。这样就知道了这 4 个步骤的来历,接下来再看看这几个步骤都是做什么的?
翻译如下:
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 深思篇 | 启动时间的度量和优化