+ Load 和 +initalize 方法
有時(shí)候我們希望類(lèi)先執(zhí)行某些一次性的初始化操作再使用,NSObject根類(lèi)中有兩個(gè)可以實(shí)現(xiàn)這種初始化操作的方法,這就是+Load和+initailze方法
+Load
調(diào)用時(shí)機(jī)
對(duì)于加入運(yùn)行期系統(tǒng)的每個(gè)類(lèi)以及它的分類(lèi)來(lái)說(shuō),必定會(huì)調(diào)用此方法,而且只會(huì)被調(diào)用一次,通常是在應(yīng)用程序啟動(dòng)的時(shí)候,執(zhí)行時(shí)機(jī)在main函數(shù)之前!并且先調(diào)用父類(lèi)的+load再調(diào)用子類(lèi)的.
@implementation FatherClass
+(void)load {
NSLog(@"%s",__func__);
}
@end
@interface SonClass : FatherClass
@end
@implementation SonClass
+(void)load {
NSLog(@"%s",__func__);
}
@end
//輸出臺(tái):
+[FatherClass load]
+[SonClass load]
如果分類(lèi)中也實(shí)現(xiàn)了該方法,那么先調(diào)用本類(lèi)的再調(diào)用分類(lèi)的
@implementation FatherClass
+(void)load {
NSLog(@"%s,%@",__func__,self);
}
@end
@implementation FatherClass (category)
+(void)load {
NSLog(@"%s,%@",__func__,self);
}
@end
輸出臺(tái):
//+[FatherClass load],FatherClass
//+[FatherClass(category) load],FatherClass
如果兩個(gè)沒(méi)有繼承關(guān)系的類(lèi)都實(shí)現(xiàn)了+load方法,那么它的調(diào)用順序取決于誰(shuí)先被加到運(yùn)行期環(huán)境中

上圖中的AnyObject類(lèi)與FatherClass類(lèi)都繼承自NSObject,但是FatherClass先被加入進(jìn)運(yùn)行期環(huán)境,所以它的+load方法會(huì)先被執(zhí)行.
輸出臺(tái):
+[FatherClass load]
+[SonClass load]
+[AnyObject load]
使用注意點(diǎn):
- 在+load的調(diào)用時(shí)機(jī),系統(tǒng)還處于"脆弱"狀態(tài),雖然系統(tǒng)的庫(kù)已經(jīng)被加載進(jìn)運(yùn)行期系統(tǒng),但是我們自己編寫(xiě)的類(lèi),或者引用的其他的類(lèi)庫(kù)中的類(lèi)不一定已經(jīng)可以使用,所以在+load中要盡量避免初始化其他的對(duì)象. 比如下面的代碼就是不安全的
@implementation FatherClass
+(void)load {
NSLog(@"%s",__func__);
AnyObject *anyObject = [AnyObject new];
// use anyObject...
}
@end
當(dāng)然AnyObject這個(gè)類(lèi)使我們自己寫(xiě)的,我們可能通過(guò)Complie Sources知道它加載的順序(這不是一個(gè)好辦法),但是是用其他類(lèi)庫(kù)我們就不得而知.如果恰好在AnyObject中使用了+load方法來(lái)進(jìn)行某些初始化操作來(lái)賦予這個(gè)類(lèi)某些特性,并且這個(gè)類(lèi)被載入的晚,那么這就有問(wèn)題了.
- +load方法不像普通的方法那樣遵循繼承規(guī)則,如果一個(gè)類(lèi)本身沒(méi)有實(shí)現(xiàn)+load方法,那么無(wú)論其各級(jí)超類(lèi)是否實(shí)現(xiàn)此方法系統(tǒng)都不會(huì)調(diào)動(dòng).這句話(huà)應(yīng)該這樣理解:正常我們給一個(gè)對(duì)象或者類(lèi)發(fā)消息,如果這個(gè)對(duì)象(或類(lèi))本身沒(méi)有實(shí)現(xiàn)該方法,那么系統(tǒng)會(huì)通過(guò)isa指針找到父類(lèi)的實(shí)現(xiàn).但是+load方法不同,子類(lèi)如果沒(méi)有實(shí)現(xiàn)該方法那么也不會(huì)去父類(lèi)中找.也就是說(shuō)你實(shí)現(xiàn)了系統(tǒng)就調(diào)用,你沒(méi)實(shí)現(xiàn)就算了.但是如果在+load中顯式的調(diào)用[super load];那么就會(huì)去調(diào)用父類(lèi)方法了.
//普通方法,子類(lèi)實(shí)現(xiàn)
@implementation FatherClass
- (void)eat {
NSLog(@"%s,%@",__func__,self);
}
@end
@implementation SonClass
- (void)eat {
NSLog(@"%s,%@",__func__,self);
}
@end
SonClass *son = [SonClass new];
[son eat];
輸出臺(tái):
//-[SonClass eat],<SonClass: 0x600000017970>
//子類(lèi)未實(shí)現(xiàn)
@implementation FatherClass
- (void)eat {
NSLog(@"%s,%@",__func__,self);
}
@en
@implementation SonClass
@end
SonClass *son = [SonClass new];
[son eat];
輸出臺(tái):
//-[FatherClass eat],<SonClass: 0x600000001600>
//+load方法,子類(lèi)實(shí)現(xiàn)
@implementation FatherClass
+(void)load {
NSLog(@"%s,%@",__func__,self);
}
@end
@implementation SonClass
+(void)load {
NSLog(@"%s,%@",__func__,self);
@end
輸出臺(tái):
//+[FatherClass load],FatherClass
//+[SonClass load],SonClass
//子類(lèi)未實(shí)現(xiàn)
@implementation FatherClass
+(void)load {
NSLog(@"%s,%@",__func__,self);
}
@end
@implementation SonClass
@end
輸出臺(tái):
//+[FatherClass load],FatherClass
- 在+load方法中的實(shí)現(xiàn)務(wù)必精簡(jiǎn),盡量減少里面所執(zhí)行的操作,因?yàn)檎麄€(gè)應(yīng)用在執(zhí)行+load方法時(shí)都會(huì)阻塞,如果在+load中進(jìn)行繁雜的代碼,那么應(yīng)用程序在執(zhí)行期間就會(huì)變得無(wú)響應(yīng),不要調(diào)用可能會(huì)加鎖的方法.實(shí)際上但凡是通過(guò)+load方法實(shí)現(xiàn)的某些任務(wù),基本上都做得不對(duì),真正的用途僅在于調(diào)試程序,比如可以再分類(lèi)中實(shí)現(xiàn)+load來(lái)看該分類(lèi)是否已經(jīng)正確載入系統(tǒng)中.
+initialize
調(diào)用時(shí)機(jī)
對(duì)于每個(gè)類(lèi)來(lái)說(shuō),該方法會(huì)在程序第一次使用該類(lèi)或者該類(lèi)的子類(lèi)時(shí)被調(diào)用,并且只會(huì)調(diào)用一次.如果子類(lèi)沒(méi)有實(shí)現(xiàn),那么會(huì)調(diào)用父類(lèi)的該方法
//子類(lèi)實(shí)現(xiàn)
@implementation FatherClass
+ (void)initialize {
NSLog(@"%s,%@",__func__,self);
}
- (void)eat {
NSLog(@"%s,%@",__func__,self);
}
@end
@implementation SonClass
+ (void)initialize {
NSLog(@"%s,%@",__func__,self);
}
@end
SonClass *son = [SonClass new];
[son eat];
輸出臺(tái):
//+[FatherClass initialize],FatherClass
//+[SonClass initialize],SonClass
//子類(lèi)不實(shí)現(xiàn)
@implementation FatherClass
+ (void)initialize {
NSLog(@"%s,%@",__func__,self);
}
- (void)eat {
NSLog(@"%s,%@",__func__,self);
}
@end
@implementation SonClass
@end
SonClass *son = [SonClass new];
[son eat];
輸出臺(tái):
//+[FatherClass initialize],FatherClass
//+[FatherClass initialize],SonClass
我們發(fā)現(xiàn)子類(lèi)如果實(shí)現(xiàn)了就走子類(lèi)的方法,子類(lèi)沒(méi)有實(shí)現(xiàn)就走父類(lèi)的方法.這與普通的方法是相同的,都遵循集成規(guī)則,這個(gè)與+load不同.
那我們?nèi)绻幌胍驗(yàn)樽宇?lèi)而調(diào)用到父類(lèi)的方法該怎么辦呢?
@implementation FatherClass
+ (void)initialize {
if (self == [FatherClass class]) {
NSLog(@"%s,%@",__func__,self);
}
}
@end
輸出臺(tái)
//+[FatherClass initialize],FatherClass
+load與+initalize方法的區(qū)別
- +initalize 是惰性調(diào)用,只有當(dāng)給該類(lèi)或者該類(lèi)的派生類(lèi)被使用時(shí)才會(huì)被調(diào)用.
- +load方法,應(yīng)用會(huì)阻塞并等待所有類(lèi)的+load執(zhí)行完才會(huì)繼續(xù)執(zhí)行.
- +initalize方法是線(xiàn)程安全的.所以不用擔(dān)心對(duì)該類(lèi)第一次發(fā)消息的線(xiàn)程問(wèn)題.
- +load不遵循繼承規(guī)則
- +load方法運(yùn)行環(huán)境不是安全的,但是+initalize方法運(yùn)行時(shí)可以調(diào)用任何類(lèi)的任何方法;