16.iOS应用启动(三):镜像文件的读取和加载

本文基于 dyld-832.7.3objc4-818.2 源码

前言

在上一文iOS应用启动(二):环境配置与runtime初始化我们提到了 _dyld_objc_notify_register,本文就将深入对镜像文件的读取map_images与加载loadClass进行解读。

一、 _dyld_objc_notify_register

_dyld_objc_notify_register(&map_images, load_images, unmap_image);

_dyld_objc_notify_register
  • 这里有两个重要的函数:

    • map_images

      • 管理文件中和动态库中的所有符号:class protocol selector category

    • load_images

      • 执行 +load 方法

  • 进行了镜像文件的读取与加载

这里有个一个小细节

我们看到这里调用 &map_images 这里用了函数的地址,因为这里非常重要,为了保持整个过程的同步。

二、 map_images

2.1 map_images_nolock

2.2 _read_images

这里的源码非常长,但是Apple贴心的加了Log我们可以从Log找下线索

  • 整体流程:

    • 条件控制进行第一次加载

    • 修复预编译阶段的 @selector 混乱问题

    • 错误混乱的类处理

    • 修复重映射一些没有被镜像文件加再进来的类

    • 修复一些消息

    • 当类中有协议的时候,readProtocol

    • 修复没有被加载的协议

    • 处理分类

    • 类的加载相关处理

修复:主要是指对地址进行修复,将MachO中的地址修复为dyld处理后的地址

2.3 循环调用 readClass 加载类

在这里添加Log可以发现,自定义的类是最后加载的:

这里如果想过滤系统的可以参考下面的代码:

2.4 readClass 源码中的坑

下图中的代码,看似进行了Class的ro rw的操作但实际运行断点的时候根本不会执行,千万不要被源码误导了。

如图三个断点:

跳过了中间的断点

2.5 寻找类相关的流程

在源码中找到了可能的地方,添加断点:

实现 非懒加载类(实现类+load方法或者静态实例)

Realize non-lazy classes (for +load methods and static instances)

Realize newly-resolved future classes

运行后只输出了:

而在其中也发现来到了另一个重要的流程:realizeClassWithoutSwift

关于这部分内容之前在研究msgSend的过程中有顺便进行了探索,可以在类的实现与初始化一文中进行深入了解。

补充:懒加载类 & 非懒加载类

2.5中提到了 非懒加载类,那么什么是 非懒加载类,什么是 懒加载类

懒加载类

数据加载推迟到第一次接收第一次消息的时候

非懒加载类

map_image 的时候加载所有类数据。

三、 load_images

  • 这里流程就比较简单了

    • 加载分类

    • 查找所有 load 方法

    • 调用 load 方法

流程图

Last updated

Was this helpful?