简介
在前东家的工作项目中,遇到过使用 +load
进行 Method Swizzling
相关配置的情况,当时大概查了下 +load
方法的特点和用法,也没有太在意;后来看资料发现还有个 +initialize
方法,与 +load
有相似之处但区别也很大,于是趁当下有闲暇时间,系统阅览文档后写下学习笔记,以作日后回顾参考。
共同点
看过官方文档以及别人的博客后觉得,它们共同点不大,若是从使用角度去看,可以认为都可以用作类的初始化设定。但由于两者的调用次数和调用时机乃至调用时的外部环境(如其他类是否已初始化)都有区别,因此使用场景需加以区分。
调用时序
+load
+load
是应用开始运行,对应类(class)或者类别(category)被加载到 runtime 时调用,而且早在 main
函数被调用前。而且在每次加载,不同类的 +load
调用顺序是不确定的,因此方法里的代码不能依赖于某个其他类的 +load
调用。除此之外,其特点可以归结为以下:
- 只被调用一次。
- 如果项目中有父类,子类以及类别,并且每个文件中都实现了
+load
,其调用顺序为:父类 -> 子类 -> 父类类别 -> 子类类别。 - 如果父类实现了
+load
而子类没有实现,父类的+load
也会被调用,因为子类被 runtime 加载理所当然地需要父类被加载。因此我们不需要也不应该调用[super load]
。
+initialize
懒加载是 +initialize
与 +load
很大的一个区别。它的懒是在该类在应用中第一次接收消息前(如第一次调用 [Class alloc]
)才被调用。如果该类在项目中没使用过,就永远不会被调用。其特点归结如下:
- 可能会被调用不止一次。会被多次调用的情况有两种:
- 子类没有实现
+initialize
,同时在项目中子类第一次接收消息比父类的早时; - 子类调用
[super initialize]
- 子类没有实现
为了避免方法内代码被多次调用,苹果文档建议检查方法调用时类的类型:
+ (void)initialize {
if (self == [ClassName self]) {
// ... do the initialization ...
}
}
- 调用顺序:父类 -> 子类
- 线程安全。例如父类在线程A处在
+initialize
方法执行中,子类在线程B触发父类的+initialize
时需要等待线程A的执行完才会执行。因为会导致线程阻塞,方法里不应该执行复杂耗时的操作。
例子
通过代码例子能跟直观地了解二者的特性与调用时序:
// headers
// 父类
@interface Animal : NSObject
@end
// 子类
@interface Bird : NSObject
@end
// 父类类别
@interface Animal (Talkable)
@end
// 子类类别
@interface Bird (Talkable)
@end
//********** 分隔线 **********//
// implementation
// 父类
@implementation Animal
+ (void)load
{
NSLog(@"Animal load called");
}
+ (void)initialize
{
NSLog(@"Animal initialize called");
}
@end
// 子类
@implementation Bird
+ (void)load
{
NSLog(@"Bird load called");
}
+ (void)initialize
{
NSLog(@"Bird initialize called");
}
@end
// 父类类别
@implementation Animal (Talkable)
+ (void)load
{
NSLog(@"Animal category load called");
}
@end
// 子类类别
@implementation Bird (Talkable)
+ (void)load
{
NSLog(@"Bird category load called");
}
@end
然后在项目某个地方调用,例如在 application:didFinishLaunchingWithOptions:
:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
Bird *bird = [[Bird alloc] init];
Animal *animal = [[Animal alloc] init];
return YES;
}
运行应用后控制台输出:
Animal load called
Bird load called
Animal category load called
Bird category load called
Animal initialize called
Bird initialize called
要验证其他情况,在子类调用 super
以及调整下对象创建顺序即可,这里不再赘述。
小结
两者对比之下,+load
的使用场景比 +initialize
更加广泛。因为只被调用一次的特性,而且调用时机早,+load
很适合进行 Method Swizzling
的操作。详细源代码实现,可在参考中查阅。