load方法
通過查看NSObject類 可以看到:load方法是NSObject類中的第一個方法

通過Apple的官方文檔 我們可以看到load方法的特點:
當類被引用進項目的時候就會執(zhí)行l(wèi)oad函數(shù)(在main函數(shù)開始執(zhí)行之前),與這個類是否被用到無關,每個類的load函數(shù)只會自動調(diào)用一次.由于load函數(shù)是系統(tǒng)自動加載的,因此不需要調(diào)用父類的load函數(shù),否則父類的load函數(shù)會多次執(zhí)行。
- 當父類和子類都實現(xiàn)load函數(shù)時,父類的load方法執(zhí)行順序要優(yōu)先于子類
- 當子類未實現(xiàn)load方法時,不會調(diào)用父類load方法
- 類中的load方法執(zhí)行順序要優(yōu)先于類別(Category)
- 當有多個類別(Category)都實現(xiàn)了load方法,這幾個load方法都會執(zhí)行,但執(zhí)行順序不確定(其執(zhí)行順序與類別在Compile Sources中出現(xiàn)的順序一致)
- 當然當有多個不同的類的時候,每個類load 執(zhí)行順序與其在Compile Sources出現(xiàn)的順序一致
為了驗證,我們在工程中新建一個Person類,繼承自NSObject,然后重寫它的load方法,但并不去使用這個類
#import "Person.h"
@implementation Person
+(void)load {
NSLog(@"%s",__func__);
}
@end
運行程序我們可以看到系統(tǒng)依然執(zhí)行了load方法里面的內(nèi)容:
2019-05-22 14:43:41.329890+0800 ZGQTest[971:23304] +[Person load]
我們再創(chuàng)建一個Animal類,也繼承自NSObject
#import "Animal.h"
@implementation Animal
+(void)load {
NSLog(@"%s",__func__);
}
@end
像上面那樣再次運行程序,打印結果為
2019-05-22 15:11:47.529401+0800 ZGQTest[1172:42356] +[Animal load]
2019-05-22 15:11:47.531099+0800 ZGQTest[1172:42356] +[Person load]
此時兩個類的load方法都執(zhí)行了,但是我們是先創(chuàng)建的Person類,后創(chuàng)建的Animal類,為什么會先打印Animal類的load方法呢?

原因就在于這兩個類在Compile Sources中出現(xiàn)的順序,我們在工程設置中打開Build Phases 找到Compile Sources,可以看到Animal類是在Person類前面的

我們將這兩個類的順序調(diào)換,重新運行程序,就會發(fā)現(xiàn)這兩個類的load方法執(zhí)行的順序也發(fā)生了改變
2019-05-22 15:36:21.623734+0800 ZGQTest[1273:56530] +[Person load]
2019-05-22 15:36:21.624246+0800 ZGQTest[1273:56530] +[Animal load]
我們可以得出類的load方法執(zhí)行的順序是根據(jù)類文件在Compile Sources出現(xiàn)的順序而決定的
此時我們再新建一個Student類,繼承自Person,也重寫它的load方法,但依然不使用這個類
#import "Person.h"
@interface Student : Person
@end
#import "Student.h"
@implementation Student
+(void)load {
NSLog(@"%s",__func__);
}
@end
運行程序,打印結果為
2019-05-22 15:50:34.831903+0800 ZGQTest[1373:66105] +[Person load]
2019-05-22 15:50:34.832777+0800 ZGQTest[1373:66105] +[Animal load]
2019-05-22 15:50:34.832971+0800 ZGQTest[1373:66105] +[Student load]
可以看到Student類的load方法是最后執(zhí)行的,此時它在Compile Sources中的位置也是排在Person和Animal后面,那么我們手動將Student類拖到這兩個類前面,再次運行程序:
[圖片上傳中...(WX20190522-155318.png-b0a464-1558511609240-0)]
打印結果為:
2019-05-22 15:53:52.584261+0800 ZGQTest[1404:68341] +[Person load]
2019-05-22 15:53:52.585259+0800 ZGQTest[1404:68341] +[Student load]
2019-05-22 15:53:52.585732+0800 ZGQTest[1404:68341] +[Animal load]
可以看到Student類的順序雖然排到了最前面,但是它的load方法依然會在Person類的load方法之后執(zhí)行,這就驗證了
當父類和子類都實現(xiàn)load函數(shù)時,父類的load方法執(zhí)行順序要優(yōu)先于子類
此時我們分別為Person和Student新建兩個category,并在category中重寫它們的load方法
#import "Person+Load.h"
@implementation Person (Load)
+ (void)load {
NSLog(@"%s",__func__);
}
@end
#import "Student+Load.h"
@implementation Student (Load)
+ (void)load {
NSLog(@"%s",__func__);
}
@end
運行結果為:
2019-05-22 16:10:13.026845+0800 ZGQTest[1525:79697] +[Person load]
2019-05-22 16:10:13.027907+0800 ZGQTest[1525:79697] +[Student load]
2019-05-22 16:10:13.028180+0800 ZGQTest[1525:79697] +[Animal load]
2019-05-22 16:10:13.028743+0800 ZGQTest[1525:79697] +[Student(Load) load]
2019-05-22 16:10:13.029152+0800 ZGQTest[1525:79697] +[Person(Load) load]
此時Compile Sources中類的順序是這樣的:

根據(jù)這個順序可以得到結論:
- 分類中的load方法總會在本類中的load方法執(zhí)行完之后再執(zhí)行
- 分類中的load方法不會被本類的繼承關系而影響,是按照Compile Sources中裝載的順序執(zhí)行的(Student類是Person的子類,但是 [Student(Load) load]方法比[Person(Load) load]先執(zhí)行)
- 分類的load方法會在所有本類的load方法都執(zhí)行完之后才執(zhí)行(雖然Student和Person的category文件是在Animal類前面的,但它們的load方法依然會在Animal的load方法之后執(zhí)行)
我們再給Student類添加一個category,并且重寫load方法,運行程序,結果為
2019-05-22 16:26:11.609162+0800 ZGQTest[1647:90389] +[Person load]
2019-05-22 16:26:11.610395+0800 ZGQTest[1647:90389] +[Student load]
2019-05-22 16:26:11.610620+0800 ZGQTest[1647:90389] +[Animal load]
2019-05-22 16:26:11.610928+0800 ZGQTest[1647:90389] +[Student(Load2) load]
2019-05-22 16:26:11.611162+0800 ZGQTest[1647:90389] +[Student(Load) load]
2019-05-22 16:26:11.611570+0800 ZGQTest[1647:90389] +[Person(Load) load]
此時在Compile Sources中Student+Load2.m是在Student+Load.m之前的,所以對于同一個本類的分類來說,它們的load方法執(zhí)行順序,是按照Compile Sources中的順序來決定的。(通過給Person類增加一個load2的category可以看到所有的分類都是根據(jù)裝載順序來執(zhí)行l(wèi)oad方法的,不止針對于本類)
注:在本類和多個category中如果有相同的方法,那么當你執(zhí)行這個方法時只會執(zhí)行最后一個加載的category中的方法,load方法并沒有遵守這個特質(zhì)
initialize方法
initialize方法 是NSObject中的第二個方法,
通過官方文檔我們可以看到initialize的特點:
initialize在類或者其子類的第一個方法被調(diào)用前調(diào)用。即使類文件被引用進項目,但是沒有使用,initialize不會被調(diào)用。由于是系統(tǒng)自動調(diào)用,也不需要再調(diào)用 [super initialize] ,否則父類的initialize會被多次執(zhí)行。假如這個類放到代碼中,而這段代碼并沒有被執(zhí)行,這個函數(shù)是不會被執(zhí)行的。
- 父類的initialize方法會比子類先執(zhí)行
- 當子類未實現(xiàn)initialize方法時,會調(diào)用父類initialize方法,子類實現(xiàn)initialize方法時,會覆蓋父類initialize方法.
- 當有多個Category都實現(xiàn)了initialize方法,會覆蓋類中的方法,只執(zhí)行一個(會執(zhí)行Compile Sources 列表中最后一個Category 的initialize方法)
我們將工程中的Person類重寫initialize方法,運行程序后發(fā)現(xiàn)打印內(nèi)容和之前一樣,只有這幾個類的load方法,并沒有執(zhí)行initialize方法
#import "Person.h"
@implementation Person
+(void)load {
NSLog(@"%s",__func__);
}
+(void)initialize {
NSLog(@"%s",__func__);
}
@end
然后我們在工程的ViewController文件中,引入Person并初始化一個Person對象
- (void)viewDidLoad {
[super viewDidLoad];
Person *p = [[Person alloc] init];
}
此時的打印結果為:
2019-05-22 20:07:05.011716+0800 ZGQTest[2196:918218] +[Person initialize]
可以看到Person的initialize方法已經(jīng)被調(diào)用了
此時我們將剛才的Person類換成它的子類Student來執(zhí)行(注意是替換,也就是說這次的代碼中沒有用到Person類,而且Student類中還沒有重寫initialize方法),打印結果為
2019-05-22 20:28:37.797186+0800 ZGQTest[2386:932564] +[Person initialize]
2019-05-22 20:28:37.797309+0800 ZGQTest[2386:932564] +[Person initialize]
可以看到Person的initialize方法,被調(diào)用了兩次,說明了子類的initialize方法沒有實現(xiàn)時,會去調(diào)用父類的initialize方法(這一點與load方法不同,當Student類沒有實現(xiàn)load方法時,運行結果只有Person類執(zhí)行了一次load方法,并不是兩次,說明子類沒有實現(xiàn)load方法時,也不會去調(diào)用父類的load方法)
為什么會調(diào)用兩次,應該是因為創(chuàng)建子類對象時,會先創(chuàng)建父類對象,調(diào)用一遍initialize方法,創(chuàng)建子類對象時由于子類沒有實現(xiàn)initialize方法,所以再次調(diào)用了父類的
接下來我們把 Student類的initialize也重寫,運行后:
2019-05-22 20:46:28.761129+0800 ZGQTest[2442:947984] +[Person initialize]
2019-05-22 20:46:28.761253+0800 ZGQTest[2442:947984] +[Student initialize]
此時子類和父類的initialize方法都執(zhí)行了,此時Student的initialize方法就覆蓋了父類的,
如果我們想讓某一個類(比如父類Person)的initialize方法只調(diào)用一次,可以這樣:
+ (void)initialize
{
if (self == [Person class]) {
NSLog(@"%s",__func__);
}
}
此時[Person initialize] 只打印了一次
接下來我們在Person+Load方法中實現(xiàn)initialize方法
打印結果為
2019-05-22 21:07:01.541884+0800 ZGQTest[2660:964029] +[Person(Load) initialize]
2019-05-22 21:07:01.542007+0800 ZGQTest[2660:964029] +[Student initialize]
可以看到分類中的initialize方法會覆蓋本類中的initialize方法
我們再在Student+Load方法中實現(xiàn)initialize方法,結果為
2019-05-22 21:08:50.725242+0800 ZGQTest[2685:965488] +[Person(Load) initialize]
2019-05-22 21:08:50.725389+0800 ZGQTest[2685:965488] +[Student(Load) initialize]
此時我們在剛才的所有分類中實現(xiàn)initialize方法,可以看到initialize方法總是會互相覆蓋,分類首先會覆蓋本類,然后分類之間會根據(jù)Compile Sources中出現(xiàn)的順序進行覆蓋(但是跟load方法不同的是,不管分類在Compile Sources中的順序如何,父類的initialize方法肯定會早于子類的initialize方法執(zhí)行)
load方法會在main函數(shù)之前調(diào)用,initialize方法會在main函數(shù)之后調(diào)用,我們可以在main函數(shù)中加一個打印來驗證
int main(int argc, char * argv[]) {
NSLog(@"%s",__func__);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
此時的運行結果為
2019-05-22 21:41:53.014826+0800 ZGQTest[3004:985822] +[Person load]
2019-05-22 21:41:53.016625+0800 ZGQTest[3004:985822] +[Animal load]
2019-05-22 21:41:53.016907+0800 ZGQTest[3004:985822] main
2019-05-22 21:41:53.118183+0800 ZGQTest[3004:985822] +[Person initialize]
2019-05-22 21:41:53.118375+0800 ZGQTest[3004:985822] +[Student initialize]
根據(jù)官方文檔我們可以看粗load方法和initialize方法的異同:
相同點:
- 方法只會被調(diào)用一次
- 內(nèi)部都使用了鎖,是線程安全的
不同點:
- load是只要類所在文件被引用就會被調(diào)用,而initialize是在類或者其子類的第一個方法被調(diào)用前調(diào)用。所以如果類沒有被引用到項目中,就不會有l(wèi)oad調(diào)用;但即使類文件被引用進來,但是沒有使用,那么initialize也不會被調(diào)用。
- load方法通常用來進行Method Swizzle,initialize方法一般用于初始化全局變量或靜態(tài)變量。