簡介
在前東家的工作項目中匾南,遇到過使用 +load
進行 Method Swizzling
相關配置的情況啃匿,當時大概查了下 +load
方法的特點和用法,也沒有太在意蛆楞;后來看資料發(fā)現(xiàn)還有個 +initialize
方法溯乒,與 +load
有相似之處但區(qū)別也很大,于是趁當下有閑暇時間豹爹,系統(tǒng)閱覽文檔后寫下學習筆記裆悄,以作日后回顧參考。
共同點
看過官方文檔以及別人的博客后覺得帅戒,它們共同點不大灯帮,若是從使用角度去看,可以認為都可以用作類的初始化設定逻住。但由于兩者的調(diào)用次數(shù)和調(diào)用時機乃至調(diào)用時的外部環(huán)境(如其他類是否已初始化)都有區(qū)別钟哥,因此使用場景需加以區(qū)分。
調(diào)用時序
+load
+load
是應用開始運行瞎访,對應類(class)或者類別(category)被加載到 runtime 時調(diào)用腻贰,而且早在 main
函數(shù)被調(diào)用前。而且在每次加載扒秸,不同類的 +load
調(diào)用順序是不確定的播演,因此方法里的代碼不能依賴于某個其他類的 +load
調(diào)用。除此之外伴奥,其特點可以歸結為以下:
- 只被調(diào)用一次写烤。
- 如果項目中有父類,子類以及類別拾徙,并且每個文件中都實現(xiàn)了
+load
洲炊,其調(diào)用順序為:父類 -> 子類 -> 父類類別 -> 子類類別。 - 如果父類實現(xiàn)了
+load
而子類沒有實現(xiàn),父類的+load
也會被調(diào)用暂衡,因為子類被 runtime 加載理所當然地需要父類被加載询微。因此我們不需要也不應該調(diào)用[super load]
。
+initialize
懶加載是 +initialize
與 +load
很大的一個區(qū)別狂巢。它的懶是在該類在應用中第一次接收消息前(如第一次調(diào)用 [Class alloc]
)才被調(diào)用撑毛。如果該類在項目中沒使用過,就永遠不會被調(diào)用唧领。其特點歸結如下:
- 可能會被調(diào)用不止一次藻雌。會被多次調(diào)用的情況有兩種:
- 子類沒有實現(xiàn)
+initialize
,同時在項目中子類第一次接收消息比父類的早時疹吃; - 子類調(diào)用
[super initialize]
- 子類沒有實現(xiàn)
為了避免方法內(nèi)代碼被多次調(diào)用蹦疑,蘋果文檔建議檢查方法調(diào)用時類的類型:
+ (void)initialize {
if (self == [ClassName self]) {
// ... do the initialization ...
}
}
- 調(diào)用順序:父類 -> 子類
- 線程安全。例如父類在線程A處在
+initialize
方法執(zhí)行中萨驶,子類在線程B觸發(fā)父類的+initialize
時需要等待線程A的執(zhí)行完才會執(zhí)行歉摧。因為會導致線程阻塞,方法里不應該執(zhí)行復雜耗時的操作腔呜。
例子
通過代碼例子能跟直觀地了解二者的特性與調(diào)用時序:
// headers
// 父類
@interface Animal : NSObject
@end
// 子類
@interface Bird : NSObject
@end
// 父類類別
@interface Animal (Talkable)
@end
// 子類類別
@interface Bird (Talkable)
@end
//********** 分隔線 **********//
// implementation
// 父類
@implementation Animal
+ (void)load
{
NSLog(@"Animal load called");
}
+ (void)initialize
{
NSLog(@"Animal initialize called");
}
@end
// 子類
@implementation Bird
+ (void)load
{
NSLog(@"Bird load called");
}
+ (void)initialize
{
NSLog(@"Bird initialize called");
}
@end
// 父類類別
@implementation Animal (Talkable)
+ (void)load
{
NSLog(@"Animal category load called");
}
@end
// 子類類別
@implementation Bird (Talkable)
+ (void)load
{
NSLog(@"Bird category load called");
}
@end
然后在項目某個地方調(diào)用叁温,例如在 application:didFinishLaunchingWithOptions:
:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
Bird *bird = [[Bird alloc] init];
Animal *animal = [[Animal alloc] init];
return YES;
}
運行應用后控制臺輸出:
Animal load called
Bird load called
Animal category load called
Bird category load called
Animal initialize called
Bird initialize called
要驗證其他情況,在子類調(diào)用 super
以及調(diào)整下對象創(chuàng)建順序即可核畴,這里不再贅述膝但。
小結
兩者對比之下,+load
的使用場景比 +initialize
更加廣泛谤草。因為只被調(diào)用一次的特性跟束,而且調(diào)用時機早,+load
很適合進行 Method Swizzling
的操作丑孩。詳細源代碼實現(xiàn)冀宴,可在參考中查閱。