在 Objective-C 中,+load 和 +initialize 是两个特殊的类方法,用于在类加载或首次使用时执行初始化代码。它们在运行时系统(Runtime)中有特定的调用时机和顺序,尤其在涉及分类(Category)和继承时,行为有明显差异。
下面从区别、调用顺序、继承场景下的行为三个方面详细说明:
一、+load 与 +initialize 的核心区别
二、Category 中的调用顺序
1. +load 的调用顺序
先调用父类的
+load,再调用子类的+load所有类的
+load调用完后,才调用分类的+load同一类的多个分类:按编译顺序(链接顺序)调用(不确定,依赖 build phase 中的文件顺序)
示例:
// ClassA.m + (void)load { NSLog(@"ClassA load"); } // ClassA+Cat1.m + (void)load { NSLog(@"ClassA+Cat1 load"); } // ClassA+Cat2.m + (void)load { NSLog(@"ClassA+Cat2 load"); }输出可能是:
ClassA load ClassA+Cat1 load ClassA+Cat2 load(具体 Cat1/Cat2 顺序取决于链接顺序)
2. +initialize 的调用顺序
只有在类或其子类首次收到消息时才会触发
分类中的
+initialize会被忽略!⚠️ 分类中定义的
+initialize不会被 runtime 调用。因为+initialize是通过消息发送机制触发的,而分类会覆盖原类的方法(包括+initialize),但通常你不应该在分类中重写+initialize,这会导致原类的+initialize丢失。如果子类没有实现
+initialize,则会调用父类的+initialize如果子类实现了
+initialize,那么:子类首次使用 → 调用子类的
+initialize同时,父类的
+initialize可能已经被调用过了(当父类首次使用时)
📌 注意:如果子类没有显式使用父类,父类的
+initialize可能由子类触发!
例如:
// Parent.m
+ (void)initialize { NSLog(@"Parent initialize"); }
// Child.m
+ (void)initialize { NSLog(@"Child initialize"); }
当第一次向 Child 发消息时:
先确保
Parent已初始化 → 调用Parent +initialize再调用
Child +initialize
但如果 Child 没有实现 +initialize,则只调用 Parent +initialize(且只调一次)。
三、继承场景下的调用过程
+load 继承行为
所有类(包括父类、子类)和它们的分类都会各自调用一次
+load调用顺序:父类 → 子类 → 父类的分类 → 子类的分类(大致如此,实际是先所有类,再所有分类)
每个
+load方法独立执行,不会自动 super 调用
示例调用顺序(简化):
SuperClass load
SubClass load
SuperClass+Cat load
SubClass+Cat load
+initialize 继承行为
懒触发:只有类首次收到消息时才调用
如果子类未实现
+initialize,则使用父类的实现(但只调一次)如果子类实现了
+initialize,则:父类的
+initialize会在子类初始化前被调用(如果尚未调用)子类的
+initialize会被调用
注意:
+initialize中可通过[self class]判断当前是哪个类触发的
+ (void)initialize {
if (self == [MyClass class]) {
// 只在 MyClass 首次使用时执行
}
}
否则,如果子类继承了该方法,+initialize 会被调用两次(一次父类触发,一次子类触发),导致重复初始化。
四、最佳实践建议
✅
+load:用于 Method Swizzling、注册全局监听等必须在 main() 前完成的操作。✅
+initialize:用于类的轻量级初始化(如设置默认属性),务必加if (self == [ClassName class])判断。❌ 不要在 Category 中重写
+initialize:会覆盖原类实现,且行为不可控。❌ 避免在
+load或+initialize中调用其他类的方法:可能对方尚未加载,导致 crash。
总结
希望这能帮你彻底理清 +load 与 +initialize 的区别和使用场景!