initialize原理
+initialize方法是在類或類的子類收到第一條消息之前被調用的必怜,這里所指的消息包括實例方法和類方法的調用忽匈。也就是說+initialize方法是以懶加載的方式被調用的,如果一直沒有給一個類或他的子類發(fā)送消息冕广,那么這個類的+initialize方法是永遠不會調用的。
當我們向某個類發(fā)送消息時,runtime會調用IMP lookUpImpOrForward(...)這個函數(shù)在類中查找相應方法的實現(xiàn)或進行消息轉發(fā)慧妄,打開objc-runtime-new.h找到這個函數(shù):
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
Class curClass;
IMP imp = nil;
...
if (initialize && !cls->isInitialized()) {
// 類沒有初始化時,對類進行初始化
_class_initialize (_class_getNonMetaClass(cls, inst));
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
...
}
從中可以看到當類沒有初始化時剪芍,會調用_class_initialize(Class cls)對類進行初始化:
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;
//遞歸調用塞淹,對父類進行_class_initialize調用,確保父類的initialize方法比子類先調用
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
......
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());
}
//發(fā)送調用類方法initialize的消息
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
......
}
在這里罪裹,先是對入參的父類進行遞歸調用饱普,以確保父類優(yōu)先于子類初始化,還有一個關鍵的地方:runtime使用了發(fā)送消息objc_msgSend的方式對+initialize方法進行調用状共,這樣套耕,+initialize方法的調用就與普通方法的調用是一致的,都是走的發(fā)送消息的流程峡继,所以冯袍,如果子類沒有實現(xiàn)+initialize方法,將會沿著繼承鏈去調用父類的+initialize方法碾牌,同理康愤,分類中的+initialize方法會覆蓋原本類的方法。
雖然對每個類只會調用一次_class_initialize(Class cls)方法舶吗,但是由于+initialize方法的調用走的是消息發(fā)送的流程征冷,當某個類有多個子類時,這個類的+initialize方法有可能會被多次調用誓琼,這時检激,可能需要在+initialize方法中判斷是否是由子類調用的:
+ (void)initialize{
if (self == [ClassName class]) {
......
}
}
initialize和load的區(qū)別
- load是類加載的時候調用,initialize是首次使用類時調用(實例方法或類方法的調用)
- load是通過函數(shù)指針直接調用的腹侣,initialize走的是消息發(fā)送流程呵扛。 因此每個類的load只會自動調用一次,而initialize可能會調用多次筐带,也可能一次也不調用今穿。
- category的load與主類的load是獨立的,先調用主類的load再調用category的load伦籍;category的initialize會“覆蓋”主類的
- load一般用來類初始化操作蓝晒,比如方法替換腮出;Initialize因為延遲調用的特性,常用來做一些懶加載操作芝薇。