NSObject的load和initialize方法

前言

今天的在项目里面做版本兼容的时候,需要利用 runtime 做 Method Swizzling,一开始把 Method Swizzling 代码写在了 + (void)initialize中,后来发现了这种做法是不对的。感觉很有必要深入了解关于 NSObject的load和initialize方法。所以才有了这一篇博客。

load 与 initialize

OC 是一门面向对象的动态语言,它的类被加载和初始化的时候,可以收到方法回调,可以在适当的情况下做一些定制处理。而这正是load和initialize方法可以帮我们做到的。在我们理解这两个方法之前,先来看看官方的文档是怎么说的吧~

load

initialize

我从官方文档中总结如下:

load

在iOS上通常就是App启动时,load 方法是只要类所在文件被引用就会被调用 一个类的 load方法不用写明[super load],父类就会收到调用。且 父类(Superclass)的方法优先于子类(Subclass)的方法,类中的方法优先于类别(Category)中的方法。 对于一个类而言,没有 load 方法实现就不会调用,不会考虑对 NSObject 的继承。

initialize

initialize 是在类或者其子类的第一个方法被调用前调用。initialize 的运行过程中是能保证线程安全的。和 load 不同,即使子类不实现 initialize 方法,会把父类的实现继承过来调用一遍。注意的是在此之前,父类的方法已经被执行过一次了,同样不需要 super 调用。

代码示例验证

代码1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#import "ViewController.h"

//父类
@interface SuperClass : NSObject

@end

//子类
@interface ChildClass : SuperClass

@end

//父类的分类
@interface SuperClass (SuperClassCategory)

@end

//子类的分类
@interface ChildClass (ChildClassCategory)

@end

//实现

@implementation SuperClass

+(void)load
{
NSLog(@"%s", __FUNCTION__);
}

+(void)initialize
{
NSLog(@"%s", __FUNCTION__);
}

@end

@implementation ChildClass

+(void)load
{
NSLog(@"%s", __FUNCTION__);
}

+(void)initialize
{
NSLog(@"%s", __FUNCTION__);
}

@end

@implementation SuperClass (SuperClassCategory)

+(void)load
{
NSLog(@"%s", __FUNCTION__);
}

+(void)initialize
{
NSLog(@"%s", __FUNCTION__);
}

@end

@implementation ChildClass (ChildClassCategory)

+(void)load
{
NSLog(@"%s", __FUNCTION__);
}

+(void)initialize
{
NSLog(@"%s", __FUNCTION__);
}

@end

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
[super viewDidLoad];

}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];

}

@end

运行结果:

在上面的代码方法 - (void)viewDidLoad 里添加:

1
2
[[SuperClass alloc] init];
[[ChildClass alloc] init];

运行结果:

这个结果最基本的验证了官方文档所说的内容。

代码2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#import "ViewController.h"

//A类
@interface AClass : NSObject

@end


//A类的分类1
@interface AClass (AClassCategory1)

@end

//A类的分类2
@interface AClass (AClassCategory2)

@end

//实现

@implementation AClass

+(void)load
{
NSLog(@"%s", __FUNCTION__);
}

+(void)initialize
{
NSLog(@"%s", __FUNCTION__);
}

@end


@implementation AClass (AClassCategory1)

+(void)load
{
NSLog(@"%s", __FUNCTION__);
}

+(void)initialize
{
NSLog(@"%s", __FUNCTION__);
}

@end

@implementation AClass (AClassCategory2)

+(void)load
{
NSLog(@"%s", __FUNCTION__);
}

+(void)initialize
{
NSLog(@"%s", __FUNCTION__);
}

@end


@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
[super viewDidLoad];
[[AClass alloc] init];


}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];

}

@end

运行结果:

只有最后一个类别(Category)的+(void)initialize执行,其他两个都被隐藏。而对于+(void)load,三个都执行,并且如果Apple的文档中介绍顺序一样:先执行类自身的实现,再执行类别(Category)中的实现。

代码3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#import "ViewController.h"

//父类
@interface SuperClass : NSObject

@end

//子类
@interface ChildClass : SuperClass

@end

//实现

@implementation SuperClass

+(void)load
{
NSLog(@"%s", __FUNCTION__);
}

+(void)initialize
{
NSLog(@"%s", __FUNCTION__);
}

@end

@implementation ChildClass

@end

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
[super viewDidLoad];
[[ChildClass alloc] init];

}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];

}

@end

运行结果:

为什么会输出两句+[SuperClass initialize],而+[SuperClass load]只有一句呢?

原因很简单:

子类如果没有实现 load 的方法是不会去继承父类的方法实现的。而在子类中并没有关于 load 的定义,所以只输出一句 +[SuperClass load];

而 initialize 方法则不一样,子类没有定义 initialize 方法时,会使用父类的 initialize 方法定义。并且我们在第一次调用子类时,会先触发父类的 initialize 方法(第1句+[SuperClass initialize])。

一点点补充

  • load 调用时机比较早,运行环境有不确定因素。具体说来,在iOS上通常就是App启动时进行加载,但当load调用的时候,并不能保证所有类都加载完成且可用,所以在 load 做一些该类方法实现是比较危险的。

  • 在 initialize 方法收到调用时,运行环境基本健全。

  • load 是在app载入时调用,所以不能做太多耗时的操作,否则十分影响用户体验。

总结

+ (void)load + (void)initialize
执行时机 在程序运行后立即执行 在类的方法第一次被调时执行
若自身未定义,是否沿用父类的方法?
类别中的定义 全都执行,但后于类中的方法 覆盖类中的方法,只执行一个

端午节快乐~~!