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