iOS初探+load和+initialize
+ initialize
和 + load
是
NSObject
类的两个类方法,它们会在运行时自动调用,我们可以利用其特性做一些初始化操作。
可以先思考如下示例控制台应该输出什么:
1 | //父类 |
启动并运行程序,控制台应输出什么呢,见结尾处。
本文主要从以下几个方面来记录:
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 implementload
methods.
从上面的说明可以看出:
+ (void)initialize
消息是在该类接收到其第一个消息之前调用。关于这里的第一个消息需要特别说明一下,对于NSObject
的runtime
机制而言,其在调用NSObject
的+ (void)load
消息不被视为第一个消息,但是,如果像普通函数调用一样直接调用NSObject
的+ (void)load
消息,则会引起+ (void)initialize
的调用。反之,如果没有向NSObject
发送第一个消息,+ (void)initialize
则不会被自动调用。- 在应用程序的生命周期中,
runtime
只会向每个类发送一次+ (void)initialize
消息,如果该类是子类,且该子类中没有实现+ (void)initialize
消息,或者子类显示调用父类实现[super initialize]
, 那么则会调用其父类的实现。也就是说,父类的+ (void)initialize
可能会被调用多次。 - 如果类包含分类,且分类重写了
initialize
方法,那么则会调用分类的initialize
实现,而原类的该方法实现不会被调用,这个机制同NSObject
的其他方法(除+ (void)load
方法) 一样,即如果原类同该类的分类包含有相同的方法实现,那么原类的该方法被隐藏而无法被调用。 - 父类的
initialize
方法先于子类的initialize
方法调用。
做做下面的练习:
1 | @interface People : NSObject |
运行程序会输出什么? 答案如下:
1 | +[People initialize] |
上面示例可以看出子类会调用父类的 + initialize
方法。
如果 ViewController
内的调用换成如下的方式,
控制台输出又会是什么呢?
1 | @implementation ViewController |
输出如下:
1 | +[People initialize] |
上面示例可以看出,对于 runtime
而言,+ initialize
方法在程序生命周期内只会调用一次。
为 Student
添加分类如下:
1 | @interface Student (Score) |
调用的地方修改如下:
1 | @implementation ViewController |
那么输出是什么呢? 如下:
1 | +[People 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:
- All initializers in any framework you link to.
- All +load methods in your image.
- All C++ static initializers and C/C++ attribute(constructor) functions in your image.
- 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.
从上面的描述可以看出:
+ (void)load
会在类或者类的分类添加到Objective-c runtime
时调用,该调用发生在application:willFinishLaunchingWithOptions:
调用之前调用。- 父类的
+load
方法先于子类的+load
方法调用,类本身的+load
方法先于分类的+load
方法调用。
看看下面的示例:
示例1:
1 | //父类 |
控制台输出为:
1 | People , +[People initialize] |
示例2:
1 | //父类 |
控制台输出为:
1 | People , +[People initialize] |
当子类没有实现 +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 | People , +[People initialize] |
参考:
Objective C类方法load和initialize的区别
Objective-C中的+initialize和+load
目前已转行教育行业,欢迎加微信交流:CaryaLiu