APP的启动优化
Main 函数之前
- 移除不需要的
动态库. - 移除不需要
Objec 类, 减少selector数量 。 - 合并功能类似的类和扩展(
Category)少用分类。 - 减少 C++
constructor构造函数数量。 - 减少 ObjeC
+load,尽量用dispatch Once或Initializers代替。
Main 函数之后
- 建立启动监控组件,监控启动任务。
- 划分主线程,子线程,和延时执行等,上报启动时间(启动耗时)
- 压缩资源图片,删除无用的图片(IO操作)
- 使用 Instagram 的 Time Profiler 进行分析。
- 避免在用户看到的第一个界面(首页控制器或注册登录页面)的viewDidLoad和viewWillAppear做太多事情。
- 首页控制器或注册登录页面用纯代码方式来构建,不用 aulayout。
APP启动时间的优化,主要是针对冷启动进行优化。
一、APP的启动介绍
冷启动与热启动
- 冷启动(Cold Launch):
- 从零开始启动APP。
- 热启动(Warm Launch):
- APP已经在内存中,在后台存活着,再次点击图标启动APP
打印APP的启动时间分析
- 通过添加环境变量可以打印出APP的启动时间分析(Edit scheme -> Run -> Arguments)
DYLD_PRINT_STATISTICS设置为 1 - 如果需要更详细的信息,那就将
DYLD_PRINT_STATISTICS_DETAILS设置为1
Pre-main 的大概过程
Pre-mian 大概过程主要分为:
-
Load dylibs (
开始将程序二进制文件初始化) 当我们点击 App 图标,内核就开始做启动程序的初始化,然后交给 dyld(The Dynamic Link Editor,动态链接器); dyld 首先会读取镜像文件,然后递归的查找动态库,利用ImageLoader来将其加载到内存中。 -
Rebase
-
Bind 但是由于 ASLR 的特性,这里需要
Rebase/Bind 修复镜像中的资源指针,来指向正确的地址 -
Objc
runtime向dyld绑定了回调,当 image 加载到内存后,dyld 会通知 runtime进行处理 ImageLoader 已经将对应的镜像加载到内存,这个时候 Runtime 会调用 map_image 去解析和处理该镜像资源(注册 Objc 类、处理 category 等) -
Initializers 接下来再调用 load_image,遍历调用类的
load 方法、调用C++的构造函数属性函数、创建非基本类型的C++静态全局变量等等。
二、APP的冷启动
1. APP的冷启动3大阶段
- dyld
- 加载动态库二进制
- runtime
- 进行各种 objc结构的初始化(注册Objc类 、初始化类对象,执行+load 等等)
- main
- 执行 OC 代码。didfinish。
- 执行 OC 代码。didfinish。
总结
- APP的启动由
dyld主导,将可执行文件加载到内存,顺便加载所有依赖的动态库, 并由runtime负责加载成objc定义的结构。 - 所有初始化工作结束后,dyld就会调用
main函数。 - 接下来就是UIApplicationMain函数,AppDelegate的
application:didFinishLaunchingWithOptions:方法。
2. APP的启动 - dyld
(1) 介绍 dyld
- dyld(dynamic link editor),Apple的动态链接器,可以用来装载Mach-O文件(
可执行文件、动态库等) - 系统 kernel 做好启动程序的初始准备后,交给 dyld 负责。
(2) 启动APP时,dyld所做的事情 :
- 装载APP的可执行文件,同时会递归加载所有依赖的动态库
- 当dyld把可执行文件、动态库都装载完毕后,会通知Runtime进行下一步的处理
加载的动态库,例如以下:
- CoreGraphics.framework
- 绘图系统,常用于绘制自定义视图,纯C的API,使用Quartz2D做引擎。
- UIKit.framework
- 常用的视图框架,封装度最高,都是OC对象,处理与用户的交互。
- Foundation.framework
- Foundation框架定义了Objective-C类的基础层
- CoreFoundation.framework
- Core Foundation框架和Foundation框架紧密相关,它们为相同功能提供接口,但Foundation框架提供Objective-C接口。
- libobjc.A.dylib (
objc 和 runtime)- libauto.dylib
- libc++abi
- libSystem.dylib
- libdispatch ( GCD )
- libsystem_c ( C语言库 )
- libsystem_blocks ( Block )
- libcommonCrypto ( 加密库,比如常用的 md5 函数 )
3. APP的启动 - runtime
-
Runtime 是 Objective-C 运行时。
-
libSystem是若干个系统lib 的集合,所以它只是一个容器 lib 而已,而且它也是开源的,里面实质上就一个文件 init.c -
由 libSystem_initializer 逐步调用到了
_objc_init,这里就是objc 和 runtime 的初始化入口。
三、总结
总结 1
-
dyld 开始将程序二进制文件初始化
- 交由
ImageLoader读取 image,其中包含了我们的类、方法等各种符号。
- 交由
-
由于
runtime向dyld 绑定了回调,当 image 加载到内存后,dyld 会通知 runtime 进行处理。 -
runtime 接手后调用 map_images 做解析和处理,接下来 在load_images中调用call_load_methods,调用所有Class和Category的
+load方法. -
调用C++静态初始化器和__attribute__((constructor))修饰的函数
-
进行各种
objc结构的初始化(注册Objc类 、初始化类对象等等)
当这一切都结束时,dyld 会清理现场,将调用栈回归,只剩下:孤独的 main 函数
总结 2
1. App启动过程
-
解析Info.plist
- 加载相关信息,例如如闪屏
- 沙箱建立、权限检查
-
Mach-O加载
-
如果是胖二进制文件,寻找合适当前CPU类别的部分
-
加载所有依赖的Mach-O文件(递归调用Mach-O加载的方法)
-
定位内部、外部指针引用,例如字符串、函数等
-
执行声明为__attribute__((constructor))的C函数
-
加载类扩展(Category)中的方法
-
C++静态对象加载、调用ObjC的 +load 函数
-
-
程序执行
- 调用main()
- 调用UIApplicationMain()
- 调用applicationWillFinishLaunching