首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

iOS 冷启动

iOS 冷启动分为几个阶段,每个阶段的的过程是什么

冷启动的过程定义为:从用户点击 App 图标开始到 appDelegate didFinishLaunching 方法执行完成为止。然后 di dFinishLaunchingWithOptions 执行完成时,用户还没看到 App 的主界面,也不能使用 App,此时 App 还需要做一些初始化工作,然后完成首页请求、首页渲染等过程,用户才能真正看到数据内容并开始使用,这个时候冷启动才算完成。因此主要分为三个阶段:

Premain T1: main() 函数之前,即操作系统加载 App 可执行文件到内存,然后执行一系列的加载、链接工作,最后执行至 App 的 main() 函数

Aftermain T2:main() 函数之后,即从 main() 开始,到 appDelegate didFinishLaunching 方法执行完毕

到用户看到主界面 T3

Premain 阶段 T1

main() 之前操作系统所做的工作是把可执行文件( Mach-O 格式)加载到内存空间,然后加载动态链接器(dyld) ,再执行一系列动态链接操作和初始化操作的过程(加载、绑定、初始化方法)。加载过程--从 exec() 到 main()

真正的加载过程从 exec() 函数开始,exec() 是一个系统调用。操作系统首先为进程分配一段内存空间,然后执行如下操作:

把 App 对应的可执行文件( Mach-O 格式)加载到内存

把 Dyld 加载到内存

Dyld 进行动态链接

dyld 在各个阶段所做的事情如下

备注:image 表示一个二进制文件(可执行文件或 so 文件),里面是被编译过的符号、代码等,所以 ImageLoader 作用是将这些文件加载进内存,并且每一个文件对应一个 ImageLoader 实例来负责加载

整个事件由 dyld 主导,完成运行环境的初始化后,配合 imageLoader 将二进制文件按格式加载到内存,动态链接依赖库,并由 runtime 负责加载成 objc 定义的结构,所有初始化工作结束后,dyld 调用真正的 main 函数。因此影响 T1 时间的因素:

动态库越多,启动越慢

Objc 类、方法越多,启动越慢

Objc 的 +load 越多,启动越慢

代码瘦身

因此随着业务的迭代,要及时将废弃无用的代码和资源文件清理掉。Match-O 文件中,___TEXT:__objc_methname: 中包含了代码中的所有方法,而 __DATA__objc_selrefs 中包含了所有被使用的方法的引用,通过两个集合的差集就可以得到所有未被使用的代码(参考自 objc_cover),具体如下:

+ load 优化

过多的 +load 方法会拖慢启动速度。具体优化方法

如果可能的话,将 方法中的内容放到渲染完成后做

使用 方法代替 ,注意把逻辑移动到 时,要注意避免 重复调用问题,可以使用 让逻辑只执行一次

备注: 与 的区别

方法会在 main() 函数之前调用,而 是在类第一次使用时才会调用

方法调用优先级:父类>子类>分类,并且不会被覆盖,均会调用

调用优先级:分类>父类,父类>子类,父类的分类重写了 方法会覆盖父类的 方法。即:1. 如果分类和父类均实现了 ,则只有分类的 会被调用;2. 如果父类和子类均实现了 ,第一次引用 子类时,先调用父类的 ,再调用子类的 ;3. 如果父类实现了 ,则第一次引用子类时,会调用两次父类的

方法在 main() 函数之前调用,所有的类文件都会加载,分类也会加载

合并多个动态库

苹果建议使用更少的动态库

优化类、方法、全局变量

after main 优化

优化首屏渲染前的功能初始化

main 函数执行后到首屏渲染完成前,只处理首屏渲染相关业务。首屏渲染外的其他功能放到首屏渲染完成后去初始化

优化主线程耗时操作,防止屏幕卡顿

将耗时操作滞后、异步处理。通常的耗时操作有网络加载、编辑、存储图片和文件资源

优化耗时操作

可以使用 Xcode 自带的 Time Profiler 时间性能分析工具,可以参考 Instruments Tutorial with Swift: Getting Started

冷启动开始、结束时间点

结束时间点:结束时间点比较好确定,可以将首页某些视图元素的展示作为首页加载完成的标志

开始时间点:

以可执行文件中任意一个类的 +load 方法的执行时间为起始点

可以以 App 的进程创建时间即(exec 函数执行时间)作为冷启动的起始时间。因为系统允许我们通过 sysctl 函数获得进程的有关信息,其中就包括进程创建的时间戳

关于 +load 方法的几个 QA

重载自己 class 的 +load 方法时需不需要调用父类?

runtime 负责按继承顺序递归调用,所以我们不能调用 super

在自己 Class 的 +load 方法时能不能替换 framework 中的某个类方法的实现?

可以,因为动态链接过程中,所有依赖库加载完毕后,才会注册 Objc 类

想让一个类的 +load 方法被调用是否需要在某个地方 import 这额文件?

不需要,只要这个类的符号被编译到最后的可执行文件中,+load 方法就会被调用

Match-O 文件

对于 OSX 和 iOS 来说,Match-O 是可执行文件的格式,主要包括以下几种类型

Executable:应用的主要二进制文件

Dylib:动态链接库

Bundle:不能被链接,只能在运行时使用 dlopen 加载

Image:包含 Execuable、Dylib、Bundle

Framework: 包含Dylib、资源文件、头文件

Match-O 文件都包含 3 个段:

__TEXT: 包含 Match header,被执行的代码和只读常量(如 C 字符串),只读可执行

__DATA: 包含全局变量、静态变量,可读可写

__LINKEDIT: 包含加载程序的元数据,如函数的名称、地址,只读

iOS Runtime 是什么

Objective-C 扩展了 C 语言,并加入了面向对象特性和消息传递机制,而这个扩展的核心是一个用 C 和编译语言写的 Runtime 库。Runtime 库使我们可以在程序运行时动态的创建对象、检查对象、修改类和对象的方法。

参考

美团外卖iOS App冷启动治理

iOS App启动的奥秘

深入理解iOS App的启动过程

iOS——App启动优化分析与总结

iOS启动优化

iOS 程序 main 函数之前发生了什么

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20201023A08OCW00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券