9-4.【OC】【Category】Category + load 方法的调用顺序规则是什么?

3 阅读2分钟

在 Objective-C 中,+load 方法是一个极其特殊的机制。与普通方法遵循“分类覆盖原类”的规则不同, +load 方法是“人人有份,按序执行”

Runtime 在启动时会直接通过函数指针调用所有的 +load,而不是通过 objc_msgSend。这决定了它的调用顺序有一套严格的层级规则:


1. 核心调用顺序规则

第一阶段:所有的类(Class)

系统会先处理所有的类,再处理分类。

  1. 先父类,后子类:父类的 +load 永远先于子类执行。
  2. 同级按编译顺序:如果是两个没有继承关系的类,则按照 Xcode 中 Compile Sources 的文件排列顺序从上到下执行。

第二阶段:所有的分类(Category)

当所有类的 +load 都跑完后,系统才开始处理分类。

  1. 按编译顺序执行:分类之间没有“父子”之分。谁在 Compile Sources 中排在后面,谁的 +load 就后执行。
  2. 不覆盖原则:分类中的 +load 不会遮蔽原类中的 +load,两者都会被执行。

2. 总结流程图

  1. 父类 +load
  2. 子类 +load
  3. 原类 +load(其他无继承关系的类)
  4. 分类 A +load
  5. 分类 B +load(按编译顺序排列)

3. 为什么规则如此特殊?(底层原理)

普通方法存储在类的 method_list 中,而 +load 在 Runtime 加载镜像时会被单独提取出来,存入两个全局列表:

  • loadable_classes:存放类的 load 指针。
  • loadable_categories:存放分类的 load 指针。

call_load_methods 函数中,Runtime 会先遍历 loadable_classes(内部有递归逻辑保证先父后子),全部完成后再遍历 loadable_categories。由于它是直接通过内存地址访问函数,避开了消息转发,所以不存在“覆盖”一说。


4. +load vs +initialize

这是面试中最常被拿来对比的两个方法。它们的调用规则完全不同:

特性+load+initialize
调用时机App 启动,类加载进内存时第一次接收到消息时(懒加载)
调用次数仅一次仅一次(除非子类没实现会调父类)
调用方式直接通过地址调用通过 objc_msgSend 调用
分类影响均执行,不覆盖分类会覆盖原类的实现
安全性此时环境可能不稳,慎用相对安全

5. 注意事项与禁忌

  • 不要手动调用 [super load] :系统会自动处理继承链,手动调用会导致父类的 load 执行两次。
  • 避免耗时操作+load 会在 main 函数之前执行,过多的耗时逻辑会直接增加 App 的启动耗时(Pre-main time)。
  • 警惕依赖关系:在 +load 时,其他的类可能还没加载完成。如果你在 Class Aload 里调用 Class B 的方法,可能会出问题。