iOS程序启动过程及优化方案
2019-10-31 本文已影响0人
Dylan_J

一、准备知识
1. App打开时间
t = t1 + t2;
t1 = 系统dyld和自身App的加载时间
t2 = main函数运行到AppDelegate的
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions NS_AVAILABLE_IOS(3_0);
方法执行。
2. Mach-O 应用的主要二进制文件
包含:
(1) _TEXT : Mach Header 包含被执行的代码和只读数据.
(2) _DATA : 包含全库变量,静态变量,可读写。
(3) _LINKEDIT : 包含加载程序的元数据,比如函数的名称和地址。
3. dyld 动态链接器
程序启动运行时会依赖很多系统动态库,而系统动态库会通过dyld(动态加载器)(/usr/lib/dyld)加载到内存中,最开始系统内核读取程序可执行文件的Header段信息做一些准备工作,之后就会将工作交给dyld。由于不止一个程序需要使用系统动态库,所以不可能在每个程序加载时都去加载所有的系统动态库,为了优化程序启动速度和利用动态库缓存,苹果从iOS3.1之后,将所有系统库(私有与公有)编译成一个大的缓存文件,这就是dyld_shared_cache,该缓存文件存在iOS系统下的/System/Library/Caches/com.apple.dyld/目录下
Bundle不能被链接,只能在运行时使用dlopen()加载
二、启动过程
main()函数前:
- 系统读取App的可执行文件(Mach-O文件),从中获取dyld的路径。
- 加载dyld去初始化运行环境,开启缓存策略,加载程序相关的依赖库(其中包括可执行文件),并对这些库进行链接。
- 调用每个依赖库的初始化方法,在这一步,runtime被初始化。
- 所有依赖库初始化后,可执行文件进行初始化。
- runtime对项目中所有的类进行结构初始化,调用load方法。
- dyld返回main函数地址,main函数调用,进入程序入口。

main()函数:
- main()函数调用
- UIApplicationMain()函数调用
- UIApplicationMain函数内部:
* 创建UIApplication对象,是应用对象的象征。
* 再创建一个UIApplication的delegate对象,负责监听UIApplication的生命周期。
* 当UIApplication的生命周期发生变化的时候,会给delegate对象发送不同的消息。
* 开启Runloop保持程序一直运行。
* 加载info.plist和启动图片,判断有没有Main.storyboard.
三、优化启动时间
(1)内嵌的dylib尽可能少,或者合并起来。
(2)Rebase/Binding减少_DATA中需要修正的指针,对于OC来说减少class、selctor、category这些元数据的数据量;对于c++来说减少虚函数数量。
(3)将不必须在load()中做的推迟到initialize中。
(4)不使用xib,直接用代码加载视图。
(5)release不要使用NSLog输出。
(6)启动时网络请求尽可能使用异步。
四、附带dylib加载过程
