iOS 应用启动优化简述
简单来说,一个应用的启动过程是指,用户点击应用图标开始,到用户看到该应用的第一个界面为止。具体可以分为下面几个时间段:
-
用户点击应用图标~main函数执行前
在该阶段,系统会加载可执行文件(应用的
.o
文件集合),加载动态链接库,调整 rebase 指针并进行符合绑定。Objc 会初始化,包括 Objc 相关类的注册,分类(category)的注册以及选择器(selector)的唯一性检查等。并且,初始化时,各个类的+load()
方法会被执行,且 C++ 静态全局变量会被声明等。为减少应用启动时间,相应的可以减少动态库的加载,减少 C++ 全局变量的声明,移除无用的类或方法,
+load()
中的操作可以延后的延后,或者放在+initialize()
方法中执行。 -
main函数开始执行~首屏渲染之前
int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class])); } } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. return YES; }
这个阶段实际是指从代理类的
application: didFinishLaunchingWithOptions
方法开始到所有与首屏渲染相关的代码执行完毕为止。这些代码执行的目的,可能会是去读取本地配置、请求服务器上的数据或者进行一些必要的计算等。所以为了节省时间,应当将与首屏渲染无关的操作放在首屏渲染完成后。
-
首屏渲染完成后
这个阶段实际是指首屏渲染代码完成后到
application: didFinishLaunchingWithOptions
方法结束之前的所有操作,虽然这部分的操作并不会妨碍用户看到首屏,但是如果耗时太长,会使得主线程无法响应用户手势,造成界面卡顿。所以这个阶段不宜存在大量计算或读写文件等耗时操作,可以单开一个线程执行相关操作。
类方法 +initialize() 与 +load()
+ (void)initialize;
当运行时第一次向一个类发送消息时,该方法便会被执行,并且该方法只自动执行一次且是线程安全的,其他线程向该类发送的消息都会被阻塞,直到初始化方法结束。
通常情况,不应该主动去调用该方法,但是也存在子类主动调用父类的初始化方法,所以可以使用下面的写法,来避免自己定义的初始化任务被重复执行。
+ (void)initialize {
if (self == [ClassName self]) {
// ... do the initialization ...
}
}
这里的 self
很特殊,虽然 - (instancetype)self;
声明为一个实例方法,但是可以作为类方法使用。
当类或者分类被加入到 Objective-C 运行时中时,可以实现下面的方法,对该类进行一些特殊处理,如 hook 处理。
+ (void)load;
初始化方法的执行的顺序为:
- 其所链接的库
- 其本身二进制文件中的
+load
方法 C++
静态变量初始化以及被__attribute__(constructor)
修饰的函数- 其他链接到该库的库
参考