iOS load和initialize方法詳解

iOS開發(fā)中總能看到+load和+initialize的身影,網(wǎng)上對(duì)于這兩個(gè)方法有很多解釋,但有些細(xì)節(jié)不夠清楚,不夠詳細(xì)蔬充。今天我們來詳細(xì)扒一扒這兩個(gè)方法.
下面針對(duì)對(duì)load方法的使用過程的變現(xiàn)提出一些問題补疑。
問題

1.load方法什么時(shí)候調(diào)用欣喧?
2.load方法調(diào)用原理歼秽?(是消息機(jī)制還有另有別的機(jī)制)
3.load方法調(diào)用順序应役?
4.load方法調(diào)用次數(shù)?

新建代碼 新建Person類,Person+Test1類 箩祥,Person+Test2 類

Person類代碼如下:

.h
+ (void)test;

.m
+ (void)load
{
    NSLog(@"Person +load");
}

+ (void)test
{
    NSLog(@"Person +test");
}

Person+Test1類代碼如下:

.m
+ (void)load
{
    NSLog(@"Person (Test1) +load");
}

+ (void)test
{
    NSLog(@"Person (Test1) +test");
}

Person+Test2類代碼如下:

+ (void)load
{
    NSLog(@"Person (Test2) +load");
}

+ (void)test
{
    NSLog(@"Person (Test2) +test");
}
直接運(yùn)行代碼
//輸出
Person +load
Person (Test1) +load
Person (Test2) +load
上述代碼并沒有調(diào)用Person這個(gè)類但是load方法還是執(zhí)行了說明+load方法會(huì)在runtime加載類院崇、分類時(shí)調(diào)用,通過iOS Category的本質(zhì)(一) 這篇文章,我們已經(jīng)知道,當(dāng)分類中和本類中存有相同的方法時(shí)袍祖,優(yōu)先調(diào)用分類中的方法底瓣,不調(diào)用本類中的方法,但是通過打印我們發(fā)現(xiàn)在本Demo中蕉陋,本類和兩個(gè)類別中的load方法都調(diào)用了捐凭,那么load方法的調(diào)用機(jī)制是什么呢?首先可以肯定的是不是消息機(jī)制凳鬓,因?yàn)槿绻?code>消息機(jī)制就只會(huì)調(diào)用分類中的load方法茁肠,本類中的是不會(huì)調(diào)用的。
查看源碼 源碼下載地址
來到 _objc_init 找到 load_images 方法
_objc_init.png
找到 load_images 方法的實(shí)現(xiàn)
load_images.png
來到 prepare_load_methods 方法
prepare_load_methods.png
在prepare_load_methods方法中缩举,分為兩個(gè)步驟:

1.獲取了所有類后,遍歷列表垦梆,將其中有+load方法的類加入loadable_class;
2.獲取所有的類別仅孩,遍歷列表托猩,將其中有+load方法的類加入loadable_categories.

來到 call_load_methods 方法
call_load_methods.png
call_load_methods方法中,

1.調(diào)用類的load方法辽慕,在call_class_loads方法中通過在第一步讀取prepare_load_methods步驟里的loadable_classes,遍歷列表并調(diào)用+load方法京腥。
2.調(diào)用類別的+load方法。
3.處理異常溅蛉。
來到 call_class_loads 方法

call_class_loads.png

  (*load_method)(cls, SEL_load);

這段代碼也就是說+load方法的調(diào)用是通過直接使用函數(shù)內(nèi)存地址的方式實(shí)現(xiàn)的,而不是更常見的objc_msgSend來發(fā)送消息.

也正是這句代碼造就了+load方法的最大特點(diǎn):類,父類與分類之間+load方法的調(diào)用是互不影響的.也就是,子類不會(huì)主動(dòng)調(diào)用父類的+load方法,如果類與分類都實(shí)現(xiàn)了+load',那么兩個(gè)+load方法都會(huì)被調(diào)用.`

來到 call_category_loads 方法
call_category_loads.png
通過上述對(duì)源碼的解析 我們發(fā)現(xiàn)+(load)方法是根據(jù)方法地址直接調(diào)用绞旅,并不是講過objc_msgSend函數(shù)調(diào)用,并且當(dāng)調(diào)用 load 方法時(shí)是先調(diào)用本類中的load方法温艇,再調(diào)用分類中的load方法因悲。每個(gè)類的load方法只走一次
問題1 :當(dāng)存在繼承關(guān)系時(shí)是先調(diào)用子類的load方法還是父類的load方法,
新創(chuàng)建Student類繼承自Person
.m
+ (void)load
{
    NSLog(@"Student +load");
}

輸出

Person +load
Student +load
Person (Test1) +load
Person (Test2) +load
可以看出當(dāng)存在繼承關(guān)系時(shí)默認(rèn)首先調(diào)用父類中l(wèi)oad方法勺爱,再調(diào)用子類中的load方法從源碼中也可以看出這個(gè)處理晃琳。
來到數(shù)據(jù)處理方法中,方法查找順序如下: _objc_init load_images琐鲁, prepare_load_methods卫旱, schedule_class_load
schedule_class_load .png
在該方法中會(huì)首先通過schedule_class_load(cls->superclass)確保父類中的 +load方法被加入loadable_class(如果父類有+load方法的話),從而保證父類的+load方法總是在子類之前調(diào)用围段。
也因此顾翼,在覆寫+load方法時(shí),不需要調(diào)用super方法奈泪。
問題2 :當(dāng)存在兩個(gè)沒有繼承關(guān)系類時(shí)load方法的調(diào)用順序是怎樣的呢适贸?
新建Tree類繼承NSObject灸芳,代碼實(shí)現(xiàn)如下:
+ (void)load{
    NSLog(@"樹生長的聲音 load調(diào)用");
}
來到工程中找到Build Settings 中,將Tree類的編譯順序挪動(dòng)到第一個(gè)位置
Build Settings.png
輸出
樹生長的聲音 load調(diào)用
Person +load
Student +load
Person (Test1) +load
Person (Test2) +load
load方法按照編譯先后順序調(diào)用(先編譯拜姿,先調(diào)用)
通過改變分類的編譯順序發(fā)現(xiàn)分類中的load方法也是按照編譯先后順序調(diào)用(先編譯烙样,先調(diào)用)的原則。
通過上述代碼驗(yàn)證和對(duì)源碼的分析蕊肥,得到如下總結(jié):

+load方法會(huì)在runtime加載類谒获、分類時(shí)調(diào)用每個(gè)類、分類的+load壁却,在程序運(yùn)行過程中只調(diào)用一次
+(load)方法是根據(jù)方法地址直接調(diào)用批狱,并不是講過objc_msgSend函數(shù)調(diào)用
+load方法是在main函數(shù)之前調(diào)用的

調(diào)用順序

沒有繼承關(guān)系,沒有添加類別的類展东,按照編譯先后順序調(diào)用(先編譯精耐,先調(diào)用)
存在繼承關(guān)系,并且添加類別的類琅锻,
先調(diào)用父類的+load卦停,再調(diào)用子類的+load,最后再調(diào)用分類的+load

+initialize方法詳解

首先也是提出幾個(gè)問題:
問題
  1. initialize方法什么時(shí)候調(diào)用恼蓬?
  2. initialize方法調(diào)用原理惊完?(是消息機(jī)制還有另有別的機(jī)制)
  3. initialize方法調(diào)用順序?
  4. initialize方法調(diào)用次數(shù)处硬?
創(chuàng)建Animal類小槐,代碼如下
#import "Animal.h"

@implementation Animal

+ (void)initialize{
    NSLog(@"Animal +initialize");

}
@end
直接運(yùn)行程序,發(fā)現(xiàn)并沒有調(diào)用initialize方法
來到main函數(shù)中荷辕,實(shí)現(xiàn)代碼
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Animal *anim = [[Animal alloc]init];
    }
    return 0;
}
//輸出
Animal +initialize
結(jié)論:+initialize方法會(huì)在類第一次接收到消息時(shí)調(diào)用
問題:當(dāng)存在繼承關(guān)系時(shí)+initialize怎么調(diào)用
新建Cat類繼承于Animal凿跳,代碼實(shí)現(xiàn)如下:
#import "Cat.h"

@implementation Cat

+ (void)initialize{
    NSLog(@"Cat +initialize");

}
@end
main函數(shù)修改代碼如下:
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Cat *cat = [[Cat alloc]init];
    }
    return 0;
}

運(yùn)行程序輸出如下:
Animal +initialize
Cat +initialize
結(jié)論:先調(diào)用父類的+initialize方法,再調(diào)用子類的+initialize方法疮方,
問題:+initialize方法調(diào)用機(jī)制控嗜?
新建 Animal的類別 Animal+Text01和 Animal+Text02
代碼實(shí)現(xiàn)如下
// Animal+Text01
#import "Animal+Text01.h"
#import <AppKit/AppKit.h>

@implementation Animal (Text01)
+ (void)initialize{
    NSLog(@"Animal+Text01 +initialize");
}

@end

// Animal+Text02
#import "Animal+Text02.h"
#import <AppKit/AppKit.h>

@implementation Animal (Text02)
+ (void)initialize{
    NSLog(@"Animal+Text02 +initialize");
}

@end

輸出:
Animal+Text01 +initialize
結(jié)論:典型的 objc_msgSend函數(shù)調(diào)用方式
通過上述代碼驗(yàn)證,得到如下總結(jié):

+initialize方法會(huì)在類第一次接收到消息時(shí)調(diào)用
+initialize 消息發(fā)送機(jī)制(objc_msgSend)

調(diào)用順序

先調(diào)用父類的+initialize,再調(diào)用子類的+initialize
(先初始化父類骡显,再初始化子類疆栏,每個(gè)類只會(huì)初始化1次)

對(duì)比+load 和 initialize 區(qū)別:
+initialize和+load的很大區(qū)別是,+initialize是通過objc_msgSend進(jìn)行調(diào)用的惫谤,所以有以下特點(diǎn):
如果子類沒有實(shí)現(xiàn)+initialize壁顶,會(huì)調(diào)用父類的+initialize(所以父類的+initialize可能會(huì)被調(diào)用多次)
如果分類實(shí)現(xiàn)了+initialize,就覆蓋類本身的+initialize調(diào)用
以上就是關(guān)于+load 和 + initialize 的總結(jié)溜歪,如有問題歡迎指正~

我的簡書主頁

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末若专,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蝴猪,更是在濱河造成了極大的恐慌调衰,老刑警劉巖膊爪,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異窖式,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)动壤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門萝喘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人琼懊,你說我怎么就攤上這事阁簸。” “怎么了哼丈?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵启妹,是天一觀的道長。 經(jīng)常有香客問我醉旦,道長饶米,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任车胡,我火速辦了婚禮檬输,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘匈棘。我一直安慰自己丧慈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布主卫。 她就那樣靜靜地躺著逃默,像睡著了一般。 火紅的嫁衣襯著肌膚如雪簇搅。 梳的紋絲不亂的頭發(fā)上完域,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音瘩将,去河邊找鬼筒主。 笑死,一個(gè)胖子當(dāng)著我的面吹牛鸟蟹,可吹牛的內(nèi)容都是我干的乌妙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼建钥,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼藤韵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起熊经,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤泽艘,失蹤者是張志新(化名)和其女友劉穎欲险,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體匹涮,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡天试,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了然低。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喜每。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖雳攘,靈堂內(nèi)的尸體忽然破棺而出带兜,到底是詐尸還是另有隱情,我是刑警寧澤吨灭,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布刚照,位于F島的核電站,受9級(jí)特大地震影響喧兄,放射性物質(zhì)發(fā)生泄漏无畔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一吠冤、第九天 我趴在偏房一處隱蔽的房頂上張望檩互。 院中可真熱鬧,春花似錦咨演、人聲如沸闸昨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饵较。三九已至,卻和暖如春遭赂,著一層夾襖步出監(jiān)牢的瞬間循诉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國打工撇他, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茄猫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓困肩,卻偏偏與公主長得像划纽,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锌畸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容