Objective-C 中的load和initialize方法

load方法

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


WX20190522-145025.png

通過Apple的官方文檔 我們可以看到load方法的特點(diǎn):

當(dāng)類被引用進(jìn)項(xiàng)目的時(shí)候就會(huì)執(zhí)行l(wèi)oad函數(shù)(在main函數(shù)開始執(zhí)行之前),與這個(gè)類是否被用到無關(guān),每個(gè)類的load函數(shù)只會(huì)自動(dòng)調(diào)用一次.由于load函數(shù)是系統(tǒng)自動(dòng)加載的,因此不需要調(diào)用父類的load函數(shù),否則父類的load函數(shù)會(huì)多次執(zhí)行。

  • 當(dāng)父類和子類都實(shí)現(xiàn)load函數(shù)時(shí),父類的load方法執(zhí)行順序要優(yōu)先于子類
  • 當(dāng)子類未實(shí)現(xiàn)load方法時(shí),不會(huì)調(diào)用父類load方法
  • 類中的load方法執(zhí)行順序要優(yōu)先于類別(Category)
  • 當(dāng)有多個(gè)類別(Category)都實(shí)現(xiàn)了load方法,這幾個(gè)load方法都會(huì)執(zhí)行,但執(zhí)行順序不確定(其執(zhí)行順序與類別在Compile Sources中出現(xiàn)的順序一致)
  • 當(dāng)然當(dāng)有多個(gè)不同的類的時(shí)候,每個(gè)類load 執(zhí)行順序與其在Compile Sources出現(xiàn)的順序一致

為了驗(yàn)證,我們?cè)诠こ讨行陆ㄒ粋€(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]

我們?cè)賱?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類,為什么會(huì)先打印Animal類的load方法呢?

WX20190522-151346.png

原因就在于這兩個(gè)類在Compile Sources中出現(xiàn)的順序,我們?cè)诠こ淘O(shè)置中打開Build Phases 找到Compile Sources,可以看到Animal類是在Person類前面的

WX20190522-151640.png

我們將這兩個(gè)類的順序調(diào)換,重新運(yùn)行程序,就會(huì)發(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í)我們?cè)傩陆ㄒ粋€(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后面,那么我們手動(dòng)將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方法依然會(huì)在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中類的順序是這樣的:


WX20190522-161303.png

根據(jù)這個(gè)順序可以得到結(jié)論:

  • 分類中的load方法總會(huì)在本類中的load方法執(zhí)行完之后再執(zhí)行
  • 分類中的load方法不會(huì)被本類的繼承關(guān)系而影響,是按照Compile Sources中裝載的順序執(zhí)行的(Student類是Person的子類,但是 [Student(Load) load]方法比[Person(Load) load]先執(zhí)行)
  • 分類的load方法會(huì)在所有本類的load方法都執(zhí)行完之后才執(zhí)行(雖然Student和Person的category文件是在Animal類前面的,但它們的load方法依然會(huì)在Animal的load方法之后執(zhí)行)

我們?cè)俳oStudent類添加一個(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之前的,所以對(duì)于同一個(gè)本類的分類來說,它們的load方法執(zhí)行順序,是按照Compile Sources中的順序來決定的。(通過給Person類增加一個(gè)load2的category可以看到所有的分類都是根據(jù)裝載順序來執(zhí)行l(wèi)oad方法的,不止針對(duì)于本類)

注:在本類和多個(gè)category中如果有相同的方法,那么當(dāng)你執(zhí)行這個(gè)方法時(shí)只會(huì)執(zhí)行最后一個(gè)加載的category中的方法,load方法并沒有遵守這個(gè)特質(zhì)

initialize方法

initialize方法 是NSObject中的第二個(gè)方法,
通過官方文檔我們可以看到initialize的特點(diǎn):

initialize在類或者其子類的第一個(gè)方法被調(diào)用前調(diào)用。即使類文件被引用進(jìn)項(xiàng)目,但是沒有使用,initialize不會(huì)被調(diào)用。由于是系統(tǒng)自動(dòng)調(diào)用,也不需要再調(diào)用 [super initialize] ,否則父類的initialize會(huì)被多次執(zhí)行。假如這個(gè)類放到代碼中,而這段代碼并沒有被執(zhí)行,這個(gè)函數(shù)是不會(huì)被執(zhí)行的。

  • 父類的initialize方法會(huì)比子類先執(zhí)行
  • 當(dāng)子類未實(shí)現(xiàn)initialize方法時(shí),會(huì)調(diào)用父類initialize方法,子類實(shí)現(xiàn)initialize方法時(shí),會(huì)覆蓋父類initialize方法.
  • 當(dāng)有多個(gè)Category都實(shí)現(xiàn)了initialize方法,會(huì)覆蓋類中的方法,只執(zhí)行一個(gè)(會(huì)執(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

然后我們?cè)诠こ痰腣iewController文件中,引入Person并初始化一個(gè)Person對(duì)象

- (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í),會(huì)去調(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í),也不會(huì)去調(diào)用父類的load方法)

為什么會(huì)調(diào)用兩次,應(yīng)該是因?yàn)閯?chuàng)建子類對(duì)象時(shí),會(huì)先創(chuàng)建父類對(duì)象,調(diào)用一遍initialize方法,創(chuàng)建子類對(duì)象時(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] 只打印了一次

接下來我們?cè)赑erson+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方法會(huì)覆蓋本類中的initialize方法

我們?cè)僭赟tudent+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í)我們?cè)趧偛诺乃蟹诸愔袑?shí)現(xiàn)initialize方法,可以看到initialize方法總是會(huì)互相覆蓋,分類首先會(huì)覆蓋本類,然后分類之間會(huì)根據(jù)Compile Sources中出現(xiàn)的順序進(jìn)行覆蓋(但是跟load方法不同的是,不管分類在Compile Sources中的順序如何,父類的initialize方法肯定會(huì)早于子類的initialize方法執(zhí)行)

load方法會(huì)在main函數(shù)之前調(diào)用,initialize方法會(huì)在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):

  • 方法只會(huì)被調(diào)用一次
  • 內(nèi)部都使用了鎖,是線程安全的

不同點(diǎn):

  • load是只要類所在文件被引用就會(huì)被調(diào)用,而initialize是在類或者其子類的第一個(gè)方法被調(diào)用前調(diào)用。所以如果類沒有被引用到項(xiàng)目中,就不會(huì)有l(wèi)oad調(diào)用;但即使類文件被引用進(jìn)來,但是沒有使用,那么initialize也不會(huì)被調(diào)用。
  • load方法通常用來進(jìn)行Method Swizzle,initialize方法一般用于初始化全局變量或靜態(tài)變量。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評(píng)論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,559評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,581評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,922評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,096評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,639評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,374評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,591評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,789評(píng)論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評(píng)論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,322評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,554評(píng)論 2 379