iOS - load 方法探索

[toc]

參考

load

initialize

http://www.reibang.com/p/a358a397a4ce

http://blog.csdn.net/ShengQiangLiu/article/details/50866228

code

// NSObject 有實(shí)現(xiàn)該方法
- (void)load;

objc4源碼解讀

// objc-os.mm
_objc_init();

// objc-runtime-new.mm
load_images();
        // 準(zhǔn)備(查找所有被實(shí)現(xiàn)的load方法)
        prepare_load_methods(); // Discover load methods
                /// 類 ★
                // 按(編譯)順序加載 classlist 數(shù)組中的類, 這個(gè)順序就是 BuildPhases 中 CompileSources 的順序
                classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count);
                schedule_class_load(remapClass(classlist[i])); // for循環(huán)
                        // ★★ 遞歸調(diào)用, 傳入super, 所以父類早于子類添加到數(shù)組中, 保證父類早于子類load 
                        schedule_class_load(cls->superclass);
                        // ★★ 將cls添加到 loadable_classes 數(shù)組的最后面, call_class_loads()是從前往后遍歷, 所以后添加的后加載; 
                        add_class_to_loadable_list(); 
                                method = cls->getLoadMethod(); // 獲取 load 的IMP
                                if (!method) return; // ★★ 如果類沒(méi)有實(shí)現(xiàn) +load, 則不加入數(shù)組, 后面也不會(huì)去調(diào)用 
                                loadable_classes[loadable_classes_used].cls = cls;
                                loadable_classes[loadable_classes_used].method = method;
                /// 分類 ★
                // 按(編譯)順序加載 categorylist 數(shù)組中的分類
                category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
                // for循環(huán), 將 cat 添加到 loadable_categories
                // 注意, 分類這里沒(méi)有遞歸調(diào)用, 不用管 super
                add_category_to_loadable_list(); 
                        method = _category_getLoadMethod(cat);
                        if (!method) return;
                        loadable_categories[loadable_categories_used].cat = cat;
                        loadable_categories[loadable_categories_used].method = method;
        
        // 加載
        call_load_methods(); // objc-loadmethod.mm
                // 先調(diào)用所有類的load
                call_class_loads();
                        // 從上面prepare好的 loadable_classes 數(shù)組中取出所有類
                        struct loadable_class *classes = loadable_classes;
                        // for循環(huán)該數(shù)組, 取到類的load方法的內(nèi)存地址, 賦值給函數(shù)指針load_method
                        load_method_t load_method = (load_method_t)classes[i].method;
                        // 使用函數(shù)指針, 直接調(diào)用每一個(gè)類的load方法
                        (*load_method)(cls, @selector(load));
                // 再調(diào)用所有分類的load
                call_category_loads();
                        load_method_t load_method = (load_method_t)cats[i].method;
                        (*load_method)(cls, @selector(load));
                        
結(jié)論★★:
  • 系統(tǒng)調(diào)用 +load 方法是根據(jù)方法地址直接調(diào)用, 并不是經(jīng)過(guò) objc_msgSend 函數(shù)調(diào)用。

    • 所以, 所有類的已實(shí)現(xiàn)的load 都會(huì)被調(diào)用 (未實(shí)現(xiàn)則不會(huì)調(diào)用, 也不會(huì)去調(diào)用父類的)咒锻。
    • 主類的 load 方法, 并不會(huì)被分類覆蓋蝗罗。
  • 先調(diào)用類的+load

    • 各個(gè)類之間, 按照編譯先后順序調(diào)用 (先編譯所袁,先調(diào)用)星立。
    • 調(diào)用子類的+ load之前會(huì)先調(diào)用父類的+ load
  • 再調(diào)用分類的+ load (分類的 load 是在所有主類 load 完畢之后才調(diào)用)。

    • 各個(gè)分類之間, 按照編譯先后順序調(diào)用 (先編譯勾拉,先調(diào)用)粘姜。

調(diào)用時(shí)機(jī):

+ load方法會(huì)在runtime加載類、分類時(shí)調(diào)用 [TBC ??? load 是 runtime 調(diào)用的嗎]

程序一啟動(dòng), 在main()函數(shù)執(zhí)行之前, 當(dāng)類或分類被加載到內(nèi)存時(shí)被調(diào)用乞榨。

換句話說(shuō), 這個(gè)load方法在 didFinishLaunchingWithOptions 之前就被調(diào)用了;

《Apple Document》
Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.

調(diào)用次數(shù):

每個(gè)類秽之、分類的+ load,在程序運(yùn)行過(guò)程中, 默認(rèn)會(huì)且只會(huì)執(zhí)行一次

調(diào)用必然性:

必然調(diào)用, 不管程序運(yùn)行過(guò)程中有沒(méi)有用到這個(gè)類, 都會(huì)調(diào)用load方法 (如果有實(shí)現(xiàn))

調(diào)用順序:

image

同一繼承體系下, 先加載父類, 再加載子類, 然后再加載子類的分類(按編譯順序, 先編譯先調(diào)用);
不同的類之間的加載順序: 是不確定的 按照編譯先后順序調(diào)用(先編譯, 先調(diào)用)吃既。

《Apple Document》
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.

見(jiàn)【objc4源碼解讀 - 結(jié)論】

父類

父類先于子類加載, 子類不要手動(dòng)調(diào)用 [super load] , 否則父類的load會(huì)執(zhí)行多次考榨。

load 不遵循繼承規(guī)則, 不管子類有沒(méi)有寫(xiě)load方法, 都不會(huì)去查找調(diào)用父類的load

分類

與其他方法不同, 每個(gè)類的load都是獨(dú)立的, 不存在繼承、重寫(xiě), 在Category中重寫(xiě)load函數(shù)不會(huì)替換原始類中的load, 原始類和Category中的load函數(shù)都會(huì)被執(zhí)行, 原始類的load會(huì)先被執(zhí)行, 再執(zhí)行Category中的load函數(shù)鹦倚。

當(dāng)有多個(gè) Category 都實(shí)現(xiàn)了load函數(shù), 這幾個(gè)load函數(shù)都會(huì)執(zhí)行, 按編譯順序, 先編譯先調(diào)用河质。

調(diào)用方式:

系統(tǒng)自動(dòng)調(diào)用, 不要手動(dòng)調(diào)用 (但實(shí)際也能調(diào)用)

安全性

線程安全 內(nèi)部加鎖 線程阻塞

在load方法中使用其他類是不安全的, 因?yàn)闀?huì)調(diào)用其他類的load方法, 而如果關(guān)系復(fù)雜的話, 就無(wú)法判斷出各個(gè)類的載入順序, 類只有初始化完成后, 類實(shí)例才能進(jìn)行正常使用

盡可能的精簡(jiǎn)load方法, 因?yàn)檎麄€(gè)應(yīng)用程序在執(zhí)行l(wèi)oad方法時(shí)會(huì)阻塞, 即, 程序會(huì)阻塞直到所有類的load方法執(zhí)行完畢, 才會(huì)繼續(xù)

應(yīng)用場(chǎng)景

一般的應(yīng)用場(chǎng)景是在該方法中實(shí)現(xiàn)方法交換(Method Swizzling)

面試題

Category中有l(wèi)oad方法嗎?load方法是什么時(shí)候調(diào)用的震叙?

有l(wèi)oad方法

load方法在runtime加載類掀鹅、分類的時(shí)候調(diào)用 【TBC】

load 方法能繼承嗎?★★
  • 答案

    load 方法可以繼承媒楼,但是一般情況下不會(huì)主動(dòng)去調(diào)用load方法乐尊,都是讓系統(tǒng)自動(dòng)調(diào)用。

  • 驗(yàn)證:

    先創(chuàng)建繼承自 NSObject 的 QGPerson, 實(shí)現(xiàn) +load 方法, 添加打印代碼;

    然后創(chuàng)建繼承自 QGPerson 的 QGStudent, 不實(shí)現(xiàn) +load 方法;

    然后在 main() 函數(shù)中, 手動(dòng)調(diào)用 [QGStudent load];

    會(huì)發(fā)現(xiàn) QGPerson+loadmain() 前后被調(diào)用了2次匣砖。

    可見(jiàn) +load 是存在繼承的, 如果自己沒(méi)有實(shí)現(xiàn), 可以沿著super_class調(diào)用父類的科吭。

  • 解析:

    首先, [QGStudent load]; 這樣寫(xiě)就是消息發(fā)送機(jī)制, 相當(dāng)于 objc_msgSend([QGStudent class], @selector(load));

    會(huì)根據(jù) QGStudent 的 isa 找到其元類對(duì)象, 在其元類對(duì)象中查找 load, 找不到, 再根據(jù) super_class 找到父元類對(duì)象, 從而找到并調(diào)用了父類的 +load。

load猴鲫、initialize的區(qū)別对人?
  • 調(diào)用方式

    load 是根據(jù)函數(shù)地址直接調(diào)用

    initialize 是通過(guò) objc_msgSend 調(diào)用

  • 調(diào)用時(shí)刻

    load 是 runtime 加載類、分類的時(shí)候調(diào)用(只會(huì)調(diào)用1次)

    initialize 是類第一次接收到消息的時(shí)候調(diào)用, 每一個(gè)類只會(huì) initialize 一次(父類的initialize方法可能會(huì)被調(diào)用多次)

load拂共、initialize 的調(diào)用順序牺弄?
  • load

    【見(jiàn)本文- 源碼 - 結(jié)論】

  • initialize

    先初始化父類

    再初始化子類(可能最終調(diào)用的是父類的initialize方法)

load、initialize 在category中的調(diào)用的順序宜狐?
load势告、initialize 出現(xiàn)繼承時(shí)他們之間的調(diào)用過(guò)程蛇捌?
系統(tǒng)是怎么調(diào)用 load 方法的?

不是通過(guò)消息機(jī)制, 而是直接通過(guò)函數(shù)指針調(diào)用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市咱台,隨后出現(xiàn)的幾起案子络拌,更是在濱河造成了極大的恐慌,老刑警劉巖回溺,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件春贸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡遗遵,警方通過(guò)查閱死者的電腦和手機(jī)萍恕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)车要,“玉大人允粤,你說(shuō)我怎么就攤上這事∫硭辏” “怎么了类垫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)登澜。 經(jīng)常有香客問(wèn)我阔挠,道長(zhǎng),這世上最難降的妖魔是什么脑蠕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮跪削,結(jié)果婚禮上谴仙,老公的妹妹穿的比我還像新娘。我一直安慰自己碾盐,他們只是感情好晃跺,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著毫玖,像睡著了一般掀虎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上付枫,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天烹玉,我揣著相機(jī)與錄音,去河邊找鬼阐滩。 笑死二打,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的掂榔。 我是一名探鬼主播继效,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼症杏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了瑞信?” 一聲冷哼從身側(cè)響起厉颤,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凡简,沒(méi)想到半個(gè)月后逼友,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡潘鲫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年翁逞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溉仑。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挖函,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出浊竟,到底是詐尸還是另有隱情怨喘,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布振定,位于F島的核電站必怜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏后频。R本人自食惡果不足惜梳庆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望卑惜。 院中可真熱鬧膏执,春花似錦、人聲如沸露久。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)毫痕。三九已至征峦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間消请,已是汗流浹背栏笆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梯啤,地道東北人竖伯。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親七婴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祟偷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

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