iOS中类的+load和initialize是两个比较特殊的方法。+load的方法调用比较早,在dyld启动过程中发出dyld_image_state_dependents_initialized的通知,objc-runtime 会执行load_images
这个方法,里面会按照一定的规律加载所有类的+load方法,并执行。当然这不是这一节的重点,我们主要将initialize。
下面是+initialize在objc中的调用:
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
**********************************************************************/
void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
if (reallyInitialize) {
// We successfully set the CLS_INITIALIZING bit. Initialize the class.
// Record that we're initializing this class so we can message it.
_setThisThreadIsInitializingClass(cls);
// Send the +initialize message.
// Note that +initialize is sent to the superclass (again) if
// this class doesn't implement +initialize. 2157218
if (PrintInitializing) {
_objc_inform("INITIALIZE: calling +[%s initialize]",
cls->nameForLogging());
}
// Exceptions: A +initialize call that throws an exception
// is deemed to be a complete and successful +initialize.
@try {
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: finished +[%s initialize]",
cls->nameForLogging());
}
}
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: +[%s initialize] threw an exception",
cls->nameForLogging());
}
@throw;
}
@finally {
// Done initializing.
// If the superclass is also done initializing, then update
// the info bits and notify waiting threads.
// If not, update them later. (This can happen if this +initialize
// was itself triggered from inside a superclass +initialize.)
monitor_locker_t lock(classInitLock);
if (!supercls || supercls->isInitialized()) {
_finishInitializing(cls, supercls);
} else {
_finishInitializingAfter(cls, supercls);
}
}
return;
}
else if (cls->isInitializing()) {
// We couldn't set INITIALIZING because INITIALIZING was already set.
// If this thread set it earlier, continue normally.
// If some other thread set it, block until initialize is done.
// It's ok if INITIALIZING changes to INITIALIZED while we're here,
// because we safely check for INITIALIZED inside the lock
// before blocking.
if (_thisThreadIsInitializingClass(cls)) {
return;
} else {
waitForInitializeToComplete(cls);
return;
}
}
else if (cls->isInitialized()) {
// Set CLS_INITIALIZING failed because someone else already
// initialized the class. Continue normally.
// NOTE this check must come AFTER the ISINITIALIZING case.
// Otherwise: Another thread is initializing this class. ISINITIALIZED
// is false. Skip this clause. Then the other thread finishes
// initialization and sets INITIALIZING=no and INITIALIZED=yes.
// Skip the ISINITIALIZING clause. Die horribly.
return;
}
else {
// We shouldn't be here.
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
initialize会在类收到第一条消息时调用。在
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
这段代码中,发现要实现某个类的initialize方法,一定要在父类initialize之后,若父类尚未执行过initialize这个方法,会在继承链中递归调用initialize,直到某个父类曾被发送所消息,触发终止递归的条件。而在下面的执行中会调用到 callInitialize(cls);
这行代码,我们看看callInitialize
的实现:
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
实质是调用了objc_msgSend
这个方法,走的是消息转发机制。而消息转发机制调用方法的基本机制,就是类有分类则执行分类方法,无分类走主类,主类中没有则到父类中寻找。到现在我们已经清楚了initialize的实现机制,下面看几个例子:
// parent
#import
@interface Parent : NSObject
@end
#import "Parent.h"
#import "Child.h"
@implementation Parent
+(void)initialize {
NSLog(@"parent");
}
@end
// child
#import "Parent.h"
@interface Child : Parent
@end
#import "Child.h"
@implementation Child
+(void)initialize {
NSLog(@"child");
}
@end
// ChildTwo
#import "Parent.h"
@interface ChildTwo : Parent
@end
#import "ChildTwo.h"
@implementation ChildTwo
+(void)initialize {
NSLog(@"ChildTwo");
}
@end
#import "ViewController.h"
#import "Parent.h"
#import "Child.h"
#import "ChildTwo.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Child *child = [[Child alloc]init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
ChildTwo *childTwo = [[ChildTwo alloc]init];
});
}
@end
实现3个类:Parent、 Child、 ChildTwo, Child和ChildTwo 继承自Parent,父类子类全部实现了initialize方法。我们看看执行结果:
2018-11-02 11:07:49.817869+0800 Initialize[47370:4909614] parent
2018-11-02 11:07:49.817998+0800 Initialize[47370:4909614] child
2018-11-02 11:07:52.818174+0800 Initialize[47370:4909614] ChildTwo
像Child发现消息时,Parent尚未被发送过消息,
回先执行Parent的initialize方法,然后执行Child的initialize方法,随后在几秒后向ChildTwo发送消息,此时Parent已经initialize,Parent类的initialize标志位已经为真,不会再执行initialize方法,直接执行自己的initialize方法。
我们去掉ChildTwo的initialize实现再看看:
#import "Parent.h"
@interface ChildTwo : Parent
@end
#import "ChildTwo.h"
@implementation ChildTwo
@end
执行结果为
2018-11-02 11:14:26.653112+0800 Initialize[47479:4915495] parent
2018-11-02 11:14:26.653230+0800 Initialize[47479:4915495] child
2018-11-02 11:14:29.653940+0800 Initialize[47479:4915495] parent
因为ChildTwo没有实现initialize方法,所以在收到消息时会去继承链中查找initialize实现,找到为止,所以打印的父类的initialize实现。由此可知,父类initialize方法在某种情况下是会实现多次的。