iOS中類的+load和initialize是兩個比較特殊的方法。+load的方法調(diào)用比較早,在dyld啟動過程中發(fā)出dyld_image_state_dependents_initialized的通知,objc-runtime 會執(zhí)行load_images這個方法,里面會按照一定的規(guī)律加載所有類的+load方法,并執(zhí)行。當(dāng)然這不是這一節(jié)的重點,我們主要將initialize。
下面是+initialize在objc中的調(diào)用:
/***********************************************************************
* 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會在類收到第一條消息時調(diào)用。在
// 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);
}
這段代碼中,發(fā)現(xiàn)要實現(xiàn)某個類的initialize方法,一定要在父類initialize之后,若父類尚未執(zhí)行過initialize這個方法,會在繼承鏈中遞歸調(diào)用initialize,直到某個父類曾被發(fā)送所消息,觸發(fā)終止遞歸的條件。而在下面的執(zhí)行中會調(diào)用到 callInitialize(cls);這行代碼,我們看看callInitialize的實現(xiàn):
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
實質(zhì)是調(diào)用了objc_msgSend這個方法,走的是消息轉(zhuǎn)發(fā)機制。而消息轉(zhuǎn)發(fā)機制調(diào)用方法的基本機制,就是類有分類則執(zhí)行分類方法,無分類走主類,主類中沒有則到父類中尋找。到現(xiàn)在我們已經(jīng)清楚了initialize的實現(xiàn)機制,下面看幾個例子:
// parent
#import <Foundation/Foundation.h>
@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
實現(xiàn)3個類:Parent、 Child、 ChildTwo, Child和ChildTwo 繼承自Parent,父類子類全部實現(xiàn)了initialize方法。我們看看執(zhí)行結(jié)果:
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發(fā)現(xiàn)消息時,Parent尚未被發(fā)送過消息,
回先執(zhí)行Parent的initialize方法,然后執(zhí)行Child的initialize方法,隨后在幾秒后向ChildTwo發(fā)送消息,此時Parent已經(jīng)initialize,Parent類的initialize標(biāo)志位已經(jīng)為真,不會再執(zhí)行initialize方法,直接執(zhí)行自己的initialize方法。
我們?nèi)サ鬋hildTwo的initialize實現(xiàn)再看看:
#import "Parent.h"
@interface ChildTwo : Parent
@end
#import "ChildTwo.h"
@implementation ChildTwo
@end
執(zhí)行結(jié)果為
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沒有實現(xiàn)initialize方法,所以在收到消息時會去繼承鏈中查找initialize實現(xiàn),找到為止,所以打印的父類的initialize實現(xiàn)。由此可知,父類initialize方法在某種情況下是會實現(xiàn)多次的。