前言
App启动是用户对于一个app的第一印象,因此如何使用户在最短的时间打开进入app显得格外重要。启动优化因此成为了App调优至关重要的一项。
只有具体了解了App的启动过程,我们才能对其进行优化。
App启动过程
App启动分为冷启动和热启动
- 热启动:App刚结束后再启动,有部分在内存但没有进程存在。
- 冷启动:启动时,App的进程不在系统里,需要开启新进程。
我们所做的优化都是针对于冷启动,即app第一次启动或是kill掉之后重新打开,不在内存里也没有进程存在。
APP冷启动可以概括为三个阶段
- 1、dyld:加载镜像,动态库
- 2、RunTime方法
- 3、main函数初始化
1、dyld
dyld(dynamic link editor)
,app的动态链接器,可以用来装在Mach-O文件(可执行文件、动态库等)
启动APP时,dyld所做的事情有
真正的加载过程从exec()函数开始,exec()是一个系统调用。操作系统首先为进程分配一段内存空间,然后执行如下操作:
- 1、把App对应的可执行文件加载到内存。
- 2、把Dyld加载到内存。
- 3、Dyld进行动态链接。
具体内容
- 1、加载动态库
- Dyld从主执行文件的header获取到需要加载的所依赖动态库列表,然后它需要找到每个 dylib,而应用所依赖的 dylib 文件可能会再依赖其他 dylib,所以所需要加载的是动态库列表一个递归依赖的集合
- 2、Rebase和Bind
- 1、Rebase在Image内部调整指针的指向。在过去,会把动态库加载到指定地址,所有指针和数据对于代码都是对的,而现在地址空间布局是随机化,所以需要在原来的地址根据随机的偏移量做一下修正
- 2、Bind是把指针正确地指向Image外部的内容。这些指向外部的指针被符号(symbol)名称绑定,dyld需要去符号表里查找,找到symbol对应的实现
2、RunTime方法
在Dyld阶段加载结束以后就进入了RunTime阶段
- 1、Objc setup
- 1、注册Objc类 (class registration)
- 2、把category的定义插入方法列表 (category registration)
- 3、保证每一个selector唯一 (selector uniquing)
- 2、Initializers
- 1、Objc的+load()函数
- 2、C++的构造函数属性函数
- 3、非基本类型的C++静态全局变量的创建(通常是类或结构体)
3、main函数初始化
APP的启动由dyld主导,将可执行文件加载到内存,顺便加载所有依赖的动态库 并由runtime负责加载成objc定义的结构 所有初始化工作结束后,dyld就会调用main函数 接下来就是UIApplicationMain函数,AppDelegate的application:didFinishLaunchingWithOptions:方法
这个里面往往是最占用启动时间的地方,同时也是我们最为可控的地方。
• 进入 main() 函数,启动应用。
• 执行 UIApplicationMain() 函数,创建 UIApplication 对象并设置 AppDelegate。
• 加载应用的主 UI,包括 storyboard 或 xib 文件,以及 AppDelegate 的各种生命周期方法,如 application:didFinishLaunchingWithOptions:。
4.首屏渲染阶段:
• 初始化 rootViewController,加载和渲染界面。
• 渲染完成后,用户将看到应用的首屏。
总结:iOS App冷启动的流程分为以下四步:
1. dyld 加载阶段:
• 动态链接器 dyld 负责加载应用的可执行文件及其依赖的动态库。此时,系统将会做如下工作:
• 查找应用的可执行文件和动态库
• 将它们加载到内存中
• 进行符号解析和绑定
• 执行初始化函数(如 +load 方法和静态构造函数)
2. runtime 初始化阶段:
• ObjC 运行时对类和分类进行注册。
• 执行各类 +load 方法,这个阶段还会进行一些 Swift 类的初始化。
3. main() 函数执行阶段:
• 进入 main() 函数,启动应用。
• 执行 UIApplicationMain() 函数,创建 UIApplication 对象并设置 AppDelegate。
• 加载应用的主 UI,包括 storyboard 或 xib 文件,以及 AppDelegate 的各种生命周期方法,如 application:didFinishLaunchingWithOptions:。
4. 首屏渲染阶段:
• 初始化 rootViewController,加载和渲染界面。
• 渲染完成后,用户将看到应用的首屏。
冷启动时间优化
- 减少动态库的数量:
动态库越多,dyld 加载的时间越长。
- 延迟初始化:
尽量延迟一些不必要的初始化工作,不要在启动时立即初始化所有对象。可以使用懒加载将一些初始化放到用户需要时再进行,以减轻启动阶段的负担。
- 避免 +load方法的使用:
+load 方法会在 dyld 加载阶段执行,建议用 +initialize 或者在合适的地方延迟执行初始化逻辑,避免阻塞启动流程。
4.优化AppDelegate:
application:didFinishLaunchingWithOptions: 方法应保持精简,避免在这里进行耗时的操作。将一些耗时任务放到后台队列中异步执行。
5.减少主线程阻塞:
启动阶段尽量避免主线程的耗时操作,如文件 I/O、网络请求等。将这些操作放到子线程处理,以免阻塞界面渲染。
6.预编译和瘦身:
移除未使用的代码、图片等资源,精简应用的体积,从而减少加载时间。
尽量减少 storyboard 的使用,尤其是大而复杂的 storyboard,可以分解成多个小的 storyboard 或者使用纯代码实现界面。
7.启动时的网络请求:
尽量避免在启动时进行同步的网络请求,如果必须请求,可以在启动完成后或在后台进行异步请求,以减少对启动时间的影响。
参考: