iOS中 +(void)load +(void)和initialize的區別

原文出處  http://www.jianshu.com/p/d25f691f0b07    該文對原文做了一些整理,此處尊重版權,感謝bestswifter爲我們寫出這麼好的文章



+(void)load  方法只要類所在文件被引用就會被調用,而+(void)initialize是在類或者子類的第一個方法被調用前調用。所以如何類沒有被引用到Complie Sources中就不會調用load方法,因此,load方法是在main函數之前調用的,因此,load方法總是在main函數之前調用的。


Load

如果一個可以調用Load方法,那麼他在調用這個方法前會調用父類的load方法。而且這個方法的實現不需要我們手動干預

// In Parent.m
+ (void)load {
    NSLog(@"Load Class Parent");
}

// In Child.m,繼承自Parent
+ (void)load {
    NSLog(@"Load Class Child");
}

// In Child+load.m,Child類的分類
+ (void)load {
    NSLog(@"Load Class Child+load");
}

// 運行結果:
/*
    2016-02-01 21:28:14.379 load[11789:1435378] Load Class Parent
    2016-02-01 21:28:14.380 load[11789:1435378] Load Class Child
    2016-02-01 22:28:14.381 load[11789:1435378] Load Class Child+load
*/

一般不會在此時寫相關的操作,因爲如果在load方法中實現一些操作的時候,系統還屬於脆弱時期,,如果調用別的類的方法,且該方法依賴於那個類的load方法進行初始化設置,那麼必須確保那個類的load方法已經調用過了,比如

// In Child.m
+ (void)load {
    NSLog(@"Load Class Child");

    Other *other = [Other new];
    [other originalFunc];

    // 如果不先調用other的load,下面這行代碼就無效,打印出null
    [Other printName];
}
load方法的調用順序其實有跡可循,我們看到demo的項目設置如下:

1171077-417e6180d36303a6.png.jpg


在Compile Sources中,文件的排放順序就是其裝載順序,自然也就是load方法調用的順序。這一點也證明了load方法中會自動調用父類的方法,因爲在deom的輸出結果中,Parent的load方法先於child調用,而它的裝載順序其實在Child之後

使用場景
由於調用load方法的環境很不安全,我們應該儘量減少load方法了邏輯。另一個原因是Load方法是線程安全的,它內部使用了鎖,所以我們應該避免線程阻塞在Load方法中
一個常見的使用場景是在load方法中實現Method Swizzle:

// In Other.m
+ (void)load {
    Method originalFunc = class_getInstanceMethod([self class], @selector(originalFunc));
    Method swizzledFunc = class_getInstanceMethod([self class], @selector(swizzledFunc));

    method_exchangeImplementations(originalFunc, swizzledFunc);
}
在Child類中的Load方法中,由於還沒有調用Other的load方法,所以輸出結果是"Original OutPut",而在main函數中就變成了"Swizzled OutPut",這樣就實現了方法替換
一般來說,除了Method Swizzle,別的邏輯都不應該放在Load方法中實現



initialize

這個方法在第一次給某個類發送消息是調用(比如實例化一個對象),並且只會調用一次.initlalize方法實際上是一種惰性調用,也就是說如果一個類一直沒有被用到,那它的Initialize方法就不會被調用,這一點有利於節約資源


調用規則
與load方法類似的是,在initilaize方法內部也會調用父類的方法,而且不需要我們顯示的寫出來。與Load方法不同之處在於,即使子類沒有實現initialize方法,也會調用父類的方法,這會導致一個很嚴重的問題:
// In Parent.m
+ (void)initialize {
    NSLog(@"Initialize Parent, caller Class %@", [self class]);
}

// In Child.m
// 註釋掉initialize方法

// In main.m
Child *child = [Child new];
運行後發現父類的initialize方法盡然調用了兩次:

2016-02-01 22:57:02.985 load[12772:1509345] Initialize Parent, caller Class Parent
2016-02-01 22:57:02.985 load[12772:1509345] Initialize Parent, caller Class Child
這是因爲在創建子類對象時,首先要創建父類對象,所以會調用一次父類的initilalize方法,然後創建子類時,儘管自己沒有實現initialize方法,但是還是會調用父類的方法。
雖然initialize方法對一個類而言只會調用一次,但是由於出現了兩個類,所以調用兩次符合規則,但不符合我們需求,正確的使用initialize方法的姿勢如下

// In Parent.m
+ (void)initialize {
    if (self == [Parent class]) {
        NSLog(@"Initialize Parent, caller Class %@", [self class]);
    }
}
加上判斷後,就不會因爲子類而調用到自己的Initialize方法了



總結:
1.loadinitialize方法都會在實例化對象之前調用,以main函數爲分水嶺,前者在main函數之前調用,後者在之後調用。這兩個方法會被自動調用,不能手動調用它們


2.loadinitialize方法都不會顯示的調用父類的方法而是自動調用,即使子類沒有Initialize方法也會調用父類的方法而load方法則不會調用父類

3.load方法通常用來進行Method Swizzle,initialize方法一般用於初始化全局變量或靜態變量

4.loadinitialize方法內部都使用了鎖,因此它們是線程安全的。實現要儘可能保持簡單,避免阻塞線程,不要再使用鎖。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章