load方法
通過查看NSObject類 可以看到:load方法是NSObject類中的第一個(gè)方法
通過Apple的官方文檔 我們可以看到load方法的特點(diǎn):
當(dāng)類被引用進(jìn)項(xiàng)目的時(shí)候就會執(zhí)行l(wèi)oad函數(shù)(在main函數(shù)開始執(zhí)行之前),與這個(gè)類是否被用到無關(guān),每個(gè)類的load函數(shù)只會自動調(diào)用一次.由于load函數(shù)是系統(tǒng)自動加載的闷畸,因此不需要調(diào)用父類的load函數(shù),否則父類的load函數(shù)會多次執(zhí)行。
- 當(dāng)父類和子類都實(shí)現(xiàn)load函數(shù)時(shí),父類的load方法執(zhí)行順序要優(yōu)先于子類
- 當(dāng)子類未實(shí)現(xiàn)load方法時(shí),不會調(diào)用父類load方法
- 類中的load方法執(zhí)行順序要優(yōu)先于類別(Category)
- 當(dāng)有多個(gè)類別(Category)都實(shí)現(xiàn)了load方法,這幾個(gè)load方法都會執(zhí)行,但執(zhí)行順序不確定(其執(zhí)行順序與類別在Compile Sources中出現(xiàn)的順序一致)
- 當(dāng)然當(dāng)有多個(gè)不同的類的時(shí)候,每個(gè)類load 執(zhí)行順序與其在Compile Sources出現(xiàn)的順序一致
為了驗(yàn)證奋救,我們在工程中新建一個(gè)Person類野舶,繼承自NSObject峦耘,然后重寫它的load方法砌们,但并不去使用這個(gè)類
#import "Person.h"
@implementation Person
+(void)load {
NSLog(@"%s",__func__);
}
@end
運(yùn)行程序我們可以看到系統(tǒng)依然執(zhí)行了load方法里面的內(nèi)容:
2019-05-22 14:43:41.329890+0800 ZGQTest[971:23304] +[Person load]
我們再創(chuàng)建一個(gè)Animal類贩虾,也繼承自NSObject
#import "Animal.h"
@implementation Animal
+(void)load {
NSLog(@"%s",__func__);
}
@end
像上面那樣再次運(yùn)行程序催烘,打印結(jié)果為
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]
此時(shí)兩個(gè)類的load方法都執(zhí)行了,但是我們是先創(chuàng)建的Person類缎罢,后創(chuàng)建的Animal類颗圣,為什么會先打印Animal類的load方法呢?
原因就在于這兩個(gè)類在Compile Sources中出現(xiàn)的順序,我們在工程設(shè)置中打開Build Phases 找到Compile Sources屁使,可以看到Animal類是在Person類前面的
我們將這兩個(gè)類的順序調(diào)換在岂,重新運(yùn)行程序,就會發(fā)現(xiàn)這兩個(gè)類的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)的順序而決定的
此時(shí)我們再新建一個(gè)Student類蛮寂,繼承自Person蔽午,也重寫它的load方法,但依然不使用這個(gè)類
#import "Person.h"
@interface Student : Person
@end
#import "Student.h"
@implementation Student
+(void)load {
NSLog(@"%s",__func__);
}
@end
運(yùn)行程序酬蹋,打印結(jié)果為
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í)行的及老,此時(shí)它在Compile Sources中的位置也是排在Person和Animal后面,那么我們手動將Student類拖到這兩個(gè)類前面范抓,再次運(yùn)行程序:
[圖片上傳中...(WX20190522-155318.png-b0a464-1558511609240-0)]
打印結(jié)果為:
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í)行,這就驗(yàn)證了
當(dāng)父類和子類都實(shí)現(xiàn)load函數(shù)時(shí),父類的load方法執(zhí)行順序要優(yōu)先于子類
此時(shí)我們分別為Person和Student新建兩個(gè)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
運(yùn)行結(jié)果為:
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]
此時(shí)Compile Sources中類的順序是這樣的:
根據(jù)這個(gè)順序可以得到結(jié)論:
- 分類中的load方法總會在本類中的load方法執(zhí)行完之后再執(zhí)行
- 分類中的load方法不會被本類的繼承關(guān)系而影響僧鲁,是按照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類添加一個(gè)category寞秃,并且重寫load方法,運(yùn)行程序偶惠,結(jié)果為
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]
此時(shí)在Compile Sources中Student+Load2.m是在Student+Load.m之前的春寿,所以對于同一個(gè)本類的分類來說,它們的load方法執(zhí)行順序忽孽,是按照Compile Sources中的順序來決定的绑改。(通過給Person類增加一個(gè)load2的category可以看到所有的分類都是根據(jù)裝載順序來執(zhí)行l(wèi)oad方法的,不止針對于本類)
注:在本類和多個(gè)category中如果有相同的方法兄一,那么當(dāng)你執(zhí)行這個(gè)方法時(shí)只會執(zhí)行最后一個(gè)加載的category中的方法厘线,load方法并沒有遵守這個(gè)特質(zhì)
initialize方法
initialize方法 是NSObject中的第二個(gè)方法,
通過官方文檔我們可以看到initialize的特點(diǎn):
initialize在類或者其子類的第一個(gè)方法被調(diào)用前調(diào)用瘾腰。即使類文件被引用進(jìn)項(xiàng)目皆的,但是沒有使用覆履,initialize不會被調(diào)用蹋盆。由于是系統(tǒng)自動調(diào)用费薄,也不需要再調(diào)用 [super initialize] ,否則父類的initialize會被多次執(zhí)行栖雾。假如這個(gè)類放到代碼中楞抡,而這段代碼并沒有被執(zhí)行,這個(gè)函數(shù)是不會被執(zhí)行的析藕。
- 父類的initialize方法會比子類先執(zhí)行
- 當(dāng)子類未實(shí)現(xiàn)initialize方法時(shí),會調(diào)用父類initialize方法,子類實(shí)現(xiàn)initialize方法時(shí),會覆蓋父類initialize方法.
- 當(dāng)有多個(gè)Category都實(shí)現(xiàn)了initialize方法,會覆蓋類中的方法,只執(zhí)行一個(gè)(會執(zhí)行Compile Sources 列表中最后一個(gè)Category 的initialize方法)
我們將工程中的Person類重寫initialize方法召廷,運(yùn)行程序后發(fā)現(xiàn)打印內(nèi)容和之前一樣,只有這幾個(gè)類的load方法账胧,并沒有執(zhí)行initialize方法
#import "Person.h"
@implementation Person
+(void)load {
NSLog(@"%s",__func__);
}
+(void)initialize {
NSLog(@"%s",__func__);
}
@end
然后我們在工程的ViewController文件中竞慢,引入Person并初始化一個(gè)Person對象
- (void)viewDidLoad {
[super viewDidLoad];
Person *p = [[Person alloc] init];
}
此時(shí)的打印結(jié)果為:
2019-05-22 20:07:05.011716+0800 ZGQTest[2196:918218] +[Person initialize]
可以看到Person的initialize方法已經(jīng)被調(diào)用了
此時(shí)我們將剛才的Person類換成它的子類Student來執(zhí)行(注意是替換,也就是說這次的代碼中沒有用到Person類治泥,而且Student類中還沒有重寫initialize方法)筹煮,打印結(jié)果為
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方法沒有實(shí)現(xiàn)時(shí)败潦,會去調(diào)用父類的initialize方法(這一點(diǎn)與load方法不同,當(dāng)Student類沒有實(shí)現(xiàn)load方法時(shí)准脂,運(yùn)行結(jié)果只有Person類執(zhí)行了一次load方法劫扒,并不是兩次,說明子類沒有實(shí)現(xiàn)load方法時(shí)狸膏,也不會去調(diào)用父類的load方法)
為什么會調(diào)用兩次沟饥,應(yīng)該是因?yàn)閯?chuàng)建子類對象時(shí),會先創(chuàng)建父類對象湾戳,調(diào)用一遍initialize方法闷板,創(chuàng)建子類對象時(shí)由于子類沒有實(shí)現(xiàn)initialize方法,所以再次調(diào)用了父類的
接下來我們把 Student類的initialize也重寫院塞,運(yùn)行后:
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]
此時(shí)子類和父類的initialize方法都執(zhí)行了遮晚,此時(shí)Student的initialize方法就覆蓋了父類的,
如果我們想讓某一個(gè)類(比如父類Person)的initialize方法只調(diào)用一次拦止,可以這樣:
+ (void)initialize
{
if (self == [Person class]) {
NSLog(@"%s",__func__);
}
}
此時(shí)[Person initialize] 只打印了一次
接下來我們在Person+Load方法中實(shí)現(xiàn)initialize方法
打印結(jié)果為
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方法中實(shí)現(xiàn)initialize方法县遣,結(jié)果為
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]
此時(shí)我們在剛才的所有分類中實(shí)現(xiàn)initialize方法,可以看到initialize方法總是會互相覆蓋汹族,分類首先會覆蓋本類萧求,然后分類之間會根據(jù)Compile Sources中出現(xiàn)的順序進(jìn)行覆蓋(但是跟load方法不同的是,不管分類在Compile Sources中的順序如何顶瞒,父類的initialize方法肯定會早于子類的initialize方法執(zhí)行)
load方法會在main函數(shù)之前調(diào)用夸政,initialize方法會在main函數(shù)之后調(diào)用,我們可以在main函數(shù)中加一個(gè)打印來驗(yàn)證
int main(int argc, char * argv[]) {
NSLog(@"%s",__func__);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
此時(shí)的運(yùn)行結(jié)果為
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ǎn):
- 方法只會被調(diào)用一次
- 內(nèi)部都使用了鎖榴徐,是線程安全的
不同點(diǎn):
- load是只要類所在文件被引用就會被調(diào)用守问,而initialize是在類或者其子類的第一個(gè)方法被調(diào)用前調(diào)用匀归。所以如果類沒有被引用到項(xiàng)目中,就不會有l(wèi)oad調(diào)用耗帕;但即使類文件被引用進(jìn)來穆端,但是沒有使用,那么initialize也不會被調(diào)用仿便。
- load方法通常用來進(jìn)行Method Swizzle体啰,initialize方法一般用于初始化全局變量或靜態(tài)變量。