注意这个过程都是在内核态完成的 , 这里提到了 PC 寄存器 , PC 寄存器存储了下一条指令的地址 , 程序的执行就是不断修改和读取 PC 寄存器来完成的 。
dyld创建启动闭包dyld 会首先创建启动闭包 , 闭包是一个缓存 , 用来提升启动速度的 。既然是缓存 , 那么必然不是每次启动都创建的 , 只有在重启手机或者更新/下载 App 的第一次启动才会创建 。闭包存储在沙盒的 tmp/com.apple.dyld 目录 , 清理缓存的时候切记不要清理这个目录 。
闭包是怎么提升启动速度的呢?我们先来看一下闭包里都有什么内容:
- dependends , 依赖动态库列表
- fixup:bind & rebase 的地址
- initializer-order:初始化调用顺序
- optimizeObjc: Objective C 的元数据
- 其他:main entry, uuid…

文章插图
为什么闭包能提高启动速度呢?
因为这些信息是每次启动都需要的 , 把信息存储到一个缓存文件就能避免每次都解析 , 尤其是 Objective C 的运行时数据(Class/Method...)解析非常慢 。
fixup有了闭包之后 , 就可以用闭包启动 App 了 。这时候很多动态库还没有加载进来 , 会首先对这些动态库 mmap 加载到虚拟内存里 。接着会对每个 Mach-O 做 fixup , 包括 Rebase 和 Bind 。
- Rebase:修复内部指针 。这是因为 Mach-O 在 mmap 到虚拟内存的时候 , 起始地址会有一个随机的偏移量 slide , 需要把内部的指针指向加上这个 slide 。
- Bind:修复外部指针 。这个比较好理解 , 因为像 printf 等外部函数 , 只有运行时才知道它的地址是什么 , bind 就是把指针指向这个地址 。
- __TEXT , __cstring , 存储实际的字符串"1234"
- __DATA , __cfstring , 存储 Objective C 字符串的元数据 , 每个元数据占用 32Byte , 里面有两个指针:内部指针 , 指向__TEXT , __cstring中字符串的位置;外部指针 isa , 指向类对象的 , 这就是为什么可以对 Objective C 的字符串字面量发消息的原因 。

文章插图
LibSystem InitializerBind & Rebase 之后 , 首先会执行 LibSystem 的 Initializer , 做一些最基本的初始化:
- 初始化 libdispatch
- 初始化 objc runtime , 注册 sel , 加载 category
Load & Static Initializer接下来会进行 main 函数之前的一些初始化 , 主要包括+load 和 static initializer 。这两类初始化函数都有个特点:调用顺序不确定 , 和对应文件的链接顺序有关系 。那么就会存在一个隐藏的坑:有些注册逻辑在+load 里 , 对应会有一些地方读取这些注册的数据 , 如果在+load 中读取 , 很有可能读取的时候还没有注册 。
那么 , 如何找到代码里有哪些 load 和 static initializer 呢?
在 Build Settings 里可以配置 write linkmap , 这样在生成的 linkmap 文件里就可以找到有哪些文件里包含 load 或者 static initializer:
- __mod_init_func , static initializer
- __objc_nlclslist , 实现+load 的类
- __objc_nlcatlist , 实现+load 的 Category
推荐阅读
- 抖音外卖平台代理 抖音外卖可以代理吗
- 碧螺春是什么样子的,碧螺春的品质等级
- 安吉白茶鸭的制作方法,安吉白茶的品质特点
- 白牡丹的感官品质,白茶三个等级
- 碧螺春茶的八大功能,碧螺春的品质等级
- 白茶品质评审步骤,天目湖白茶泡水颜色
- 白茶的加工过程是什么,安吉白茶的品质特点
- 冰岛茶的产地与品质,冰岛普洱茶的茶香
- 数据中台建设从数据中台的认知开始
- 曼糯古树茶品质怎么样,曼糯古树赏析
