iOS 中 load 和 initialize 方法的區(qū)別和原理

1. load

1.1 概述

先看下官網(wǎng)文檔給出的說明

The load message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.
The order of initialization is as follows:
All initializers in any framework you link to.
All +load methods in your image.
All C++ static initializers and C/C++ attribute(constructor) functions in your image.
All initializers in frameworks that link to you.
In addition:
A class’s +load method is called after all of its superclasses’ +load methods.
A category +load method is called after the class’s own +load method.
In a custom implementation of load you can therefore safely message other unrelated classes from the same image, but any load methods implemented by those classes may not have run yet.

從文檔中我們可以簡單得到:
首先我們必須在類或者分類中實(shí)現(xiàn) load 方法伴挚,這樣才能程序的初始化中被調(diào)用。
我們iOS程序初始化時(shí)异赫,我們編譯的好的類和分類都會(huì)被直接加載到內(nèi)存中返敬,在framework初始化方法調(diào)用完成后遂庄,就會(huì)開始調(diào)用我們代碼中所有類實(shí)現(xiàn)的load的方法。這個(gè)過程和類是否被調(diào)用或者引用沒有關(guān)系劲赠,只要該類參與了編譯涛目,并且實(shí)現(xiàn)了load的方法,那么他就會(huì)被調(diào)用凛澎。所以load方法的調(diào)用是在main 函數(shù)被執(zhí)行之前的霹肝。

1.2 調(diào)用順序

假設(shè)一個(gè)類,它本身塑煎、子類沫换、所有分類都實(shí)現(xiàn)了load方法,那么它的調(diào)用順序時(shí)怎么樣的最铁?
load 方法執(zhí)行規(guī)則:

  • 先執(zhí)行所有類的load方法讯赏,再執(zhí)行所有分類的load方法垮兑。
  • 執(zhí)行類的load方法時(shí),是按照參與編譯的順序漱挎,先編譯的類先執(zhí)行甥角,但是如果某個(gè)類是繼承自另一個(gè)類,那么會(huì)先執(zhí)行父類的load方法個(gè)再執(zhí)行自己的load方法识樱。
  • 執(zhí)行分類的load方法時(shí),是按照分類參與編譯的順序震束,先編譯的分類先執(zhí)行怜庸。

編譯順序,如果使用xcode,我們可以在項(xiàng)目的Build Phases --> Compile Sources查看垢村,最上面的就最先編譯割疾,我們可以拖動(dòng)文件來調(diào)整編譯順序。

1.3 方法調(diào)用原理

load方法和普通方法調(diào)用的方式不一樣嘉栓。普通方法調(diào)用是通過消息發(fā)送機(jī)制實(shí)現(xiàn)的宏榕,會(huì)先去類或元類的方法列表中查找,如果找到了方法就執(zhí)行侵佃,如果沒有找到就去父類的方法列表里面找麻昼,只要找到就會(huì)終止查找,所以只會(huì)執(zhí)行一次馋辈。
load方法調(diào)用時(shí)抚芦,每個(gè)類都是根據(jù)load方法的地址直接調(diào)用,而不會(huì)走objc_msgSend函數(shù)的方法查找流程迈螟,也就是說一個(gè)類有實(shí)現(xiàn)load方法就執(zhí)行叉抡,沒有就不執(zhí)行(沒有的話也不會(huì)去父類里面查找)。

1.4 應(yīng)用

通常會(huì)在load方法里面進(jìn)行方法交換(Method Swizzle)答毫,沒有必要話盡量不要在load方法里面實(shí)現(xiàn)過多的代碼褥民,尤其是一些耗時(shí)性的。另外也不要去直接調(diào)用其他類洗搂,因?yàn)槠渌惪赡苓€沒有加載完全消返。

2 initialize

2.1 概述

The runtime sends initialize() to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program. Superclasses receive this message before their subclasses.
The runtime sends the initialize() message to classes in a thread-safe manner. That is, initialize() is run by the first thread to send a message to a class, and any other thread that tries to send a message to that class will block until initialize() completes.
The superclass implementation may be called multiple times if subclasses do not implement initialize()—the runtime will call the inherited implementation—or if subclasses explicitly call [super initialize]. If you want to protect yourself from being run multiple times, you can structure your implementation along these lines:

+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}

Because initialize() is called in a blocking manner, it’s important to limit method implementations to the minimum amount of work necessary possible. Specifically, any code that takes locks that might be required by other classes in their initialize() methods is liable to lead to deadlocks. Therefore, you should not rely on initialize() for complex initialization, and should instead limit it to straightforward, class local initialization.
Special Considerations
initialize() is invoked only once per class. If you want to perform independent initialization for the class and for categories of the class, you should implement load() methods.

官方文檔上可知:
initialize方法是在類或它的子類收到第一條消息時(shí)被調(diào)用的,并且是線程安全的蚕脏,這里的消息就是指實(shí)例方法或類方法的調(diào)用侦副,所以所有類的initialize調(diào)用是在執(zhí)行main函數(shù)之后調(diào)用的。而且一個(gè)類只會(huì)調(diào)用一次initialize方法驼鞭。如果一個(gè)類在程序運(yùn)行過程中一直沒有被使用過秦驯,那這個(gè)類的initialize方法也就不會(huì)被調(diào)用,這一點(diǎn)和load方法是不一樣的挣棕。
另外父類一定是早于子類收到initialize 的消息的译隘,如果子類沒有實(shí)現(xiàn)initialize 方法亲桥,那么第一次收到消息時(shí),就會(huì)調(diào)用父類的initialize方法固耘。
如果子類和父類都實(shí)現(xiàn)了initialize方法题篷,那么會(huì)先調(diào)用父類的方法,然后調(diào)用子類的方法個(gè)(<u>這里注意子類中不需要寫[super initialize]來調(diào)用父類的方法厅目,通過查看源碼得知它是在底層實(shí)現(xiàn)過程中主動(dòng)調(diào)用的父類的initialize方法</u>)番枚。
需要注意的是如果在分類中實(shí)現(xiàn)了initialize方法,那么他會(huì)覆蓋掉原來類對initialize的實(shí)現(xiàn)损敷。

2.2 調(diào)用規(guī)則例子

假設(shè) Person 為基類實(shí)現(xiàn)了initialize方法葫笼, Student和Player為子類且實(shí)現(xiàn)了initialize方法, Teacher和Driver為子類但是沒有實(shí)現(xiàn) initialize方法拗馒。

Student *s = Student.new;
Person *p = Person.new;
Player *player = Player.new;
Teacher *t = Teacher.new;
Driver *d = Driver.new;

那么打印log順序?yàn)椋?/p>

+[Person initialize]
+[Student initialize]
+[Player initialize]
+[Person initialize]
+[Person initialize]
  1. 因?yàn)镾tudent 是Person的子類路星,所以Person會(huì)優(yōu)先執(zhí)行,然后才到Student.
  2. 因?yàn)镻erson已經(jīng)執(zhí)行過了initialize诱桂,所以不再執(zhí)行
  3. Person已經(jīng)執(zhí)行過洋丐,所以只會(huì)執(zhí)行Player的方法
  4. Teacher和Driver都沒有實(shí)現(xiàn)initialize,所以都直接調(diào)用了他們父類(Person)的方法

2.3 應(yīng)用

實(shí)際開發(fā)中initialize方法一般用于初始化全局變量或靜態(tài)變量挥等。雖然使用initialize要比使用load安全(因?yàn)樵谡{(diào)用initialize時(shí)所有類已經(jīng)被加載進(jìn)內(nèi)存了)友绝,但我們還是要盡量少用initialize這個(gè)方法個(gè),尤其要謹(jǐn)慎在分類中實(shí)現(xiàn)initialize方法肝劲,因?yàn)槿绻诜诸愔袑?shí)現(xiàn)了九榔,本類實(shí)現(xiàn)的initialize方法將不會(huì)被調(diào)用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涡相,一起剝皮案震驚了整個(gè)濱河市哲泊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌催蝗,老刑警劉巖切威,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異丙号,居然都是意外死亡先朦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門犬缨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喳魏,“玉大人,你說我怎么就攤上這事怀薛〈滩剩” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長创倔。 經(jīng)常有香客問我嗡害,道長,這世上最難降的妖魔是什么畦攘? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任霸妹,我火速辦了婚禮,結(jié)果婚禮上知押,老公的妹妹穿的比我還像新娘叹螟。我一直安慰自己,他們只是感情好台盯,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布首妖。 她就那樣靜靜地躺著,像睡著了一般爷恳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上象踊,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天温亲,我揣著相機(jī)與錄音,去河邊找鬼杯矩。 笑死栈虚,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的史隆。 我是一名探鬼主播魂务,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼泌射!你這毒婦竟也來了粘姜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤熔酷,失蹤者是張志新(化名)和其女友劉穎孤紧,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拒秘,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡号显,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了躺酒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片押蚤。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖羹应,靈堂內(nèi)的尸體忽然破棺而出揽碘,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布钾菊,位于F島的核電站帅矗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏煞烫。R本人自食惡果不足惜浑此,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望滞详。 院中可真熱鬧凛俱,春花似錦、人聲如沸料饥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽岸啡。三九已至原叮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間巡蘸,已是汗流浹背奋隶。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悦荒,地道東北人唯欣。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像搬味,于是被迫代替她去往敵國和親境氢。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

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