3-3.【OC】【Runtime】类的加载过程大致经历了哪些阶段?

0 阅读2分钟

在 Objective-C 中,类的加载并不是简单的“一次性到位”,而是一个从磁盘二进制文件运行时动态对象的深度加工过程。

这个过程主要由 dyld(动态链接器)和 libobjc(Runtime 库)协作完成,大致可以分为以下 5 个阶段


1. 静态映射阶段 (Mapping)

当 App 启动时,dyld 会将 App 的可执行文件(Mach-O)和依赖的动态库加载到虚拟内存中。

  • 数据来源:此时类的信息存储在 Mach-O 文件的 __DATA 段中(如 __objc_classlist__objc_methname 等)。
  • 结果:此时类只是内存中的一堆“静态字节”,指针尚未重定位。

2. 注册与读取阶段 (_read_images)

这是 libobjc 介入的第一个关键点。dyld 会通知 Runtime:“镜像(Image)已就位,请处理”。

  • Sel 注册:将所有的 SEL(方法名)注册到全局哈希表中,确保内存中同一个方法名只有一份地址。
  • 类注册:Runtime 扫描所有定义的类,将它们映射到内部的类列表(gdb_objc_realized_classes)中。
  • 协议修复:处理所有 @protocol

3. 实现与修复阶段 (Realization)

这是最核心的步骤。此时类由 class_ro_t(只读数据)升级为 class_rw_t(可读写数据)。

  • 分配空间:在堆上为类分配 class_rw_t 结构。
  • 继承链修正:递归地去实现父类和元类(Meta Class)。将 superclassisa 指针指向对应的类对象。
  • 方法列表处理:将 class_ro_t 中的原始方法、属性、协议拷贝到 class_rw_t 中。

4. 分类合并阶段 (Category Attachment)

在类实现之后,Runtime 会处理该类关联的所有 Category(分类)

  • 逻辑:将分类中的方法、属性、协议列表提取出来。
  • 插入顺序:将分类的方法列表插入到主类方法列表的前面
  • 结果:这就是为什么分类中如果有同名方法,会“覆盖”主类方法(本质上是优先找到了分类的方法)。

5. 初始化阶段 (Initialization)

这是逻辑层面的激活。

  • +load 调用:在 main 函数之前,Runtime 会遍历所有类,按照“父类 -> 子类 -> 分类”的顺序调用 +load
  • +initialize 调用:这是“懒加载”。当类第一次收到消息(即第一次调用方法)时,Runtime 会检查该类是否初始化过,如果没有,则执行 +initialize

总结全景图

阶段执行者核心产物时间点
映射dyld原始 Mach-O 数据Pre-main
读取libobjc符号哈希表、初始类表Pre-main
实现libobjcclass_rw_t 结构体Pre-main
合并libobjc包含分类的完整方法表Pre-main
逻辑激活App+load / +initializePre-main / 首次使用