初始化階段 —— load 和 initialize
load函數
原型:
1 +(void)load
當類被引用進程序的時候會執行這個函數。
在一個程序開始運行之前(在main函數開始執行之前),在庫開始被程序加載,load函數就會開始被執行。
我們開發的程序都可以認為是一個庫,但是庫又不會獨立存在(我們的程序還會引用其他庫,也可能被其他函數引用),所以庫的初始化順序可以如下:
- 初始化我們引用的庫
- 執行我們自己庫的Objective-C的load函數
- 執行C++和C的static初始化變量
- 初始化引用我們庫的其他庫
在我們的編寫的庫中,會有很多類重寫load函數,他們之間的執行順序是不確定的。
當父類和子類都實現load函數時,父類的load函數會被先執行。load函數是系統自動加載的,因此不需要調用父類的load函數,否則父類的load函數會多次執行。
在Category中寫load函數是不會替換原始類中的load函數的,原始類和Category中的load函數都會被執行,原始類的load會先被執行,再執行Category中的load函數。當有多個Category都實現了load函數,這幾個load函數執行順序不確定。
Initialize函數
原型:
1 + (void)initialize
當類第一次被執行到的時候這個函數會被執行。
如果類包含繼承關系,父類的initialize函數會比子類先執行。由於是系統自動調用,也不需要顯式的調用父類的initialize,否則父類的initialize會被多次執行。
假如這個類放到代碼中,而這段代碼並沒有被執行,這個函數是不會被執行的。
Load or Initialize
這兩個函數沒有交集,也沒有執行的先后順序,他們各自遵循着各自的調用原則。因此在寫邏輯的時候,不能有邏輯依賴load函數比initialize函數先行調用。
來看下面這種情況:
1 @interface NoneClass : NSObject 2 3 4 5 @end 6 7 8 9 @implementation NoneClass 10 11 +(void)load 12 13 { 14 15 NSLog(@"NoneClass _cmd: %@", NSStringFromSelector(_cmd)); 16 17 MyTestObject *test = [[MyTestObject alloc] init]; 18 19 } 20 21 @end 22 23 24 25 @interface MyTestObject 26 27 28 29 @end 30 31 32 33 @implementation MyTestObject 34 35 36 37 + (void) load 38 39 { 40 41 NSLog(@"MyTestObject _cmd: %@", NSStringFromSelector(_cmd)); 42 43 } 44 45 46 47 + (void)initialize 48 49 { 50 51 NSLog(@"MyTestObject _cmd: %@", NSStringFromSelector(_cmd)); 52 53 } 54 55 56 57 @end
輸出的結果:
2014-04-11 12:45:44.297 ObjectTest[1617:60b] NoneClass _cmd: load 2014-04-11 12:45:44.300 ObjectTest[1617:60b] MyTestObject _cmd: initialize 2014-04-11 12:45:44.301 ObjectTest[1617:60b] MyTestObject _cmd: load
結果分析:
由於在執行NonoClass的load函數中調用了MyTestObject的構造函數,這樣會觸發MyTestObject的initialize函數調用。而此時MyTestObject的load函數還沒有被調用。
使用場景:
將針對於類修改放在intialize中,將針對Category的修改放在load中。
但是假如我們是修改系統的類,一般會通過添加Category來添加功能,但是如果修改initialize會導致原生的intialize不會執行,所以放在load中會比較妥當。