+ initialize+ loadNSObject 类的两个类方法,它们会在运行时自动调用,我们可以利用其特性做一些初始化操作。

可以先思考如下示例控制台应该输出什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//父类
@interface People : NSObject
@end
@implementation People
+ (void)initialize {
NSLog(@"%@ , %s", [self class], __FUNCTION__);
}
@end

//子类
@interface Student : People
@end
@implementation Student
+ (void)load {
NSLog(@"%@ , %s", [self class], __FUNCTION__);
}
@end

启动并运行程序,控制台应输出什么呢,见结尾处。

本文主要从以下几个方面来记录:

initialize初探

先看看 NSObject Class Reference 中关于 +initialize 的说明:

Initializes the class before it receives its first message.

The runtime sends initialize to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program. The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses. The superclass implementation may be called multiple times if subclasses do not implement initialize—the runtime will call the inherited implementation—or if subclasses explicitly call [super initialize]. If you want to protect yourself from being run multiple times, you can structure your implementation along these lines:

1
2
3
4
5
+ (void)initialize {
if (self == [ClassName self]) {
// ... do the initialization ...
}
}

initialize is invoked only once per class. If you want to perform independent initialization for the class and for categories of the class, you should implement load methods.

从上面的说明可以看出:

  1. + (void)initialize 消息是在该类接收到其第一个消息之前调用。关于这里的第一个消息需要特别说明一下,对于 NSObjectruntime 机制而言,其在调用 NSObject+ (void)load 消息不被视为第一个消息,但是,如果像普通函数调用一样直接调用 NSObject+ (void)load 消息,则会引起 + (void)initialize 的调用。反之,如果没有向 NSObject 发送第一个消息,+ (void)initialize 则不会被自动调用。
  2. 在应用程序的生命周期中,runtime 只会向每个类发送一次 + (void)initialize 消息,如果该类是子类,且该子类中没有实现 + (void)initialize 消息,或者子类显示调用父类实现 [super initialize], 那么则会调用其父类的实现。也就是说,父类的 + (void)initialize 可能会被调用多次。
  3. 如果类包含分类,且分类重写了initialize方法,那么则会调用分类的 initialize 实现,而原类的该方法实现不会被调用,这个机制同 NSObject 的其他方法(除 + (void)load 方法) 一样,即如果原类同该类的分类包含有相同的方法实现,那么原类的该方法被隐藏而无法被调用。
  4. 父类的 initialize 方法先于子类的 initialize 方法调用。

做做下面的练习:

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
@interface People : NSObject

@end

@implementation People

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

@end

@interface Student : People

@end

@implementation Student

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

@end

@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Student *student = [[Student alloc] init];
}
@end

运行程序会输出什么? 答案如下:

1
2
+[People initialize]
+[Student initialize]

上面示例可以看出子类会调用父类的 + initialize 方法。

如果 ViewController 内的调用换成如下的方式, 控制台输出又会是什么呢?

1
2
3
4
5
6
7
8
9
10
11
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Student *student = [[Student alloc] init];
NSLog(@"viewDidLoad finished.");
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
Student *student = [[Student alloc] init];
}
@end

输出如下:

1
2
3
+[People initialize]
+[Student initialize]
viewDidLoad finished.

上面示例可以看出,对于 runtime 而言,+ initialize 方法在程序生命周期内只会调用一次。

Student 添加分类如下:

1
2
3
4
5
6
7
8
9
10
@interface Student (Score)

@end

@implementation Student (Score)

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

调用的地方修改如下:

1
2
3
4
5
6
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Student *student = [[Student alloc] init];
}
@end

那么输出是什么呢? 如下:

1
2
+[People initialize]
+[Student(Score) initialize]

从该实例可以看出,分类的 +initialize 方法会覆盖原类中 +initialize 方法。

load初探

先看看 NSObject Class Reference 中关于 + (void)load 的说明:

Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.

The load message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.

The order of initialization is as follows:

  1. All initializers in any framework you link to.
  2. All +load methods in your image.
  3. All C++ static initializers and C/C++ attribute(constructor) functions in your image.
  4. All initializers in frameworks that link to you.

In addition:

  • A class’s +load method is called after all of its superclasses’ +load methods.
  • A category +load method is called after the class’s own +load method.

In a custom implementation of load you can therefore safely message other unrelated classes from the same image, but any load methods implemented by those classes may not have run yet.

从上面的描述可以看出:

  1. + (void)load 会在类或者类的分类添加到 Objective-c runtime 时调用,该调用发生在 application:willFinishLaunchingWithOptions: 调用之前调用。
  2. 父类的 +load 方法先于子类的 +load 方法调用,类本身的 +load 方法先于分类的 +load 方法调用。

看看下面的示例:

示例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
//父类
@interface People : NSObject
@end
@implementation People

+ (void)initialize {
NSLog(@"%@ , %s", [self class], __FUNCTION__);
}
+ (void)load {
NSLog(@"%@, %s", [self class], __FUNCTION__);
}
@end
//子类
@interface Student : People
@end
@implementation Student
+ (void)initialize {
NSLog(@"%@, %s", [self class], __FUNCTION__);
}
+ (void)load {
NSLog(@"%@, %s", [self class], __FUNCTION__);
}
@end
//子类分类
@interface Student (Score)
@end
@implementation Student (Score)
+ (void)initialize {
NSLog(@"%@, %s", [self class], __FUNCTION__);
}
+ (void)load {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
@end

控制台输出为:

1
2
3
4
5
People , +[People initialize]
People, +[People load]
Student, +[Student(Score) initialize]
Student, +[Student load]
Student +[Student(Score) load]

示例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//父类
@interface People : NSObject
@end
@implementation People
+ (void)initialize {
NSLog(@"%@ , %s", [self class], __FUNCTION__);
}
@end
//子类
@interface Student : People
@end
@implementation Student
+ (void)load {
NSLog(@"%@, %s", [self class], __FUNCTION__);
}
@end

控制台输出为:

1
2
3
People , +[People initialize]
Student , +[People initialize]
Student, +[Student load]

当子类没有实现 +initialize 而父类有其实现时,父类的实现调用了两次,且 +initialize 的调用在 +load 调用之前,这是因为我们在 +load 实现中包含 [self class] 的调用。

另外, 重写 +initialize+load 方法时,没有必要显示调用其父类的方法。

load使用示例

load使用示例1, 见 @sunnyxx 大神的博客 Notification Once, 用于给 AppDelegate 瘦身。

load使用示例2, 见博客 Method Swizzling 和 AOP 实践, 在UIViewController的 +load 时期执行IMP替换,实现AOP。


本文开始出示例控制台输出:

1
2
3
People , +[People initialize]
Student , +[People initialize]
Student , +[Student load]

参考:

Objective C类方法load和initialize的区别

Objective-C中的+initialize和+load


目前已转行教育行业,欢迎加微信交流:CaryaLiu