Objective-C 中的load和initialize方法

load方法

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


WX20190522-145025.png

通過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方法呢?

WX20190522-151346.png

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

WX20190522-151640.png

我們將這兩個(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中類的順序是這樣的:


WX20190522-161303.png

根據(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)變量。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嗽仪,一起剝皮案震驚了整個(gè)濱河市荒勇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闻坚,老刑警劉巖枕屉,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鲤氢,居然都是意外死亡搀擂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門卷玉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哨颂,“玉大人,你說我怎么就攤上這事相种⊥眨” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵寝并,是天一觀的道長箫措。 經(jīng)常有香客問我,道長衬潦,這世上最難降的妖魔是什么斤蔓? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮镀岛,結(jié)果婚禮上弦牡,老公的妹妹穿的比我還像新娘。我一直安慰自己漂羊,他們只是感情好驾锰,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著走越,像睡著了一般椭豫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天赏酥,我揣著相機(jī)與錄音喳整,去河邊找鬼。 笑死今缚,一個(gè)胖子當(dāng)著我的面吹牛算柳,可吹牛的內(nèi)容都是我干的低淡。 我是一名探鬼主播姓言,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蔗蹋!你這毒婦竟也來了何荚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤猪杭,失蹤者是張志新(化名)和其女友劉穎餐塘,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體皂吮,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡戒傻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜂筹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片需纳。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖艺挪,靈堂內(nèi)的尸體忽然破棺而出不翩,到底是詐尸還是另有隱情,我是刑警寧澤麻裳,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布口蝠,位于F島的核電站,受9級特大地震影響津坑,放射性物質(zhì)發(fā)生泄漏妙蔗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一疆瑰、第九天 我趴在偏房一處隱蔽的房頂上張望灭必。 院中可真熱鬧,春花似錦乃摹、人聲如沸禁漓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽播歼。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秘狞,已是汗流浹背叭莫。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烁试,地道東北人雇初。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像减响,于是被迫代替她去往敵國和親靖诗。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345