iOS開發(fā)之 runtime(36) :load 方法加載

logo

本系列博客是本人的源碼閱讀筆記猖毫,如果有 iOS 開發(fā)者在看 runtime 的,歡迎大家多多交流释簿。

前言

load 方法中的最后一個函數(shù):

_dyld_objc_notify_register(&map_2_images, load_images, unmap_image);

告訴我們蕉斜,第一步是 map_2_images,第二步就是 load_images怕膛。筆者前面的文章也已經(jīng)分析過熟嫩,將文件或者類從 mach-o 里讀出來后,進(jìn)行 remap 等操作后放入相應(yīng)的 hashmap 中褐捻,其實是做了第一步:緩存掸茅,為了方便后面調(diào)用。而第二步 load_images柠逞,則是調(diào)用 load 方法昧狮。本文將給大家慢慢揭開 load 方法的神秘面紗。

分析

load_images 方法的實現(xiàn)如下:

void load_images(const char *path __unused, const struct mach_header *mh) {
    if (!hasLoadMethods((const headerType *)mh)) return;
    prepare_load_methods((const headerType *)mh);
    call_load_methods();
}

去掉一些無關(guān)邏輯(主要是鎖操作和注釋)可以看出其實該方法就是兩部操作:

  • 調(diào)用 load 方法之前的一些準(zhǔn)備工作
  • 開始調(diào)用 load 方法

大家可能會有一些困惑

  • load 方法之前的 prepare板壮,究竟 prepare 的是什么逗鸣?為什么要準(zhǔn)備?
  • 調(diào)用的大概順序是什么绰精?比如類 A 和 類 A 的父類都實現(xiàn)了 load 方法撒璧,甚至還有其 category 都實現(xiàn)了,那順序是怎么樣的笨使?下面筆者帶大家抽絲剝繭看代碼卿樱。

準(zhǔn)備操作

prepare_load_methods 的代碼如下:

void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertWriting();

    classref_t *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i]));
    }

    category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = categorylist[i];
        Class cls = remapClass(cat->cls);
        if (!cls) continue;  // category for ignored weak-linked class
        realizeClass(cls);
        assert(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}

相信看過之前文章的讀者應(yīng)該對

_getObjc2NonlazyClassList

這個方法很熟悉了,作用就是獲取所有擁有 load 方法的類(class)硫椰。

_getObjc2NonlazyCategoryList

同理可證殿如,是獲取所有擁有 load 方法的 分類(category)。
所以這是第一步:取出所有的 load 方法的類或者分類最爬。

至此我們開篇提出的幾個問題涉馁,估計大家可能應(yīng)該有一點答案了:

  • 比如類 A 和 類 A 的父類都實現(xiàn)了 load 方法,甚至還有其 category 都實現(xiàn)了爱致,那順序是怎么樣的烤送?

因為先加載_getObjc2NonlazyClassList后加載_getObjc2NonlazyCategoryList所以,感覺上應(yīng)該是先調(diào)用當(dāng)前類的 load 方法糠悯,再調(diào)用 category 的 load 方法帮坚,那事實是不是這樣呢?我們繼續(xù)分析代碼互艾。
上面不管是 _getObjc2NonlazyClassList還是_getObjc2NonlazyCategoryList后面都有接下來的操作试和,我們對這兩個后面的代碼做分析:

for (i = 0; i < count; i++) {
    schedule_class_load(remapClass(classlist[i]));
}

這段,先 remap 再調(diào)用 schedule_class_load所以纫普,remap 大家很熟悉了不做過多介紹了阅悍。我們進(jìn)入 schedule_class_load 看一下:

/***********************************************************************
* prepare_load_methods
* Schedule +load for classes in this image, any un-+load-ed 
* superclasses in other images, and any categories in this image.
**********************************************************************/
// Recursively schedule +load for cls and any un-+load-ed superclasses.
// cls must already be connected.
static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;

    // Ensure superclass-first ordering
    schedule_class_load(cls->superclass);

    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}

從以上代碼可以看出來,其實 schedule_class_load 只是將 cls 通過一個方法add_class_to_loadable_list加到一個列表里,等一下!為什么又調(diào)用了自身:

   // Ensure superclass-first ordering
    schedule_class_load(cls->superclass);

是不是意味著节视,先調(diào)用父類的 load 方法拳锚,再調(diào)用自身的?寻行!

繼續(xù)分析方法 add_class_to_loadable_list

void add_class_to_loadable_list(Class cls)
{
    IMP method;

    loadMethodLock.assertLocked();

    method = cls->getLoadMethod();
    if (!method) return;  // Don't bother if cls has no +load method
    
    if (PrintLoading) {
        _objc_inform("LOAD: class '%s' scheduled for +load", 
                     cls->nameForLogging());
    }
    
    if (loadable_classes_used == loadable_classes_allocated) {
        loadable_classes_allocated = loadable_classes_allocated*2 + 16;
        loadable_classes = (struct loadable_class *)
            realloc(loadable_classes,
                              loadable_classes_allocated *
                              sizeof(struct loadable_class));
    }
    
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}

我們可以發(fā)現(xiàn)霍掺,最終將所有擁有 load 方法的類都加到靜態(tài)對象 loadable_classes中:

// List of classes that need +load called (pending superclass +load)
// This list always has superclasses first because of the way it is constructed
static struct loadable_class *loadable_classes = nil;
static int loadable_classes_used = 0;
static int loadable_classes_allocated = 0;

類似的 category 的也有類似的方法:

/***********************************************************************
* add_category_to_loadable_list
* Category cat's parent class exists and the category has been attached
* to its class. Schedule this category for +load after its parent class
* becomes connected and has its own +load method called.
**********************************************************************/
void add_category_to_loadable_list(Category cat)
{
    IMP method;

    loadMethodLock.assertLocked();

    method = _category_getLoadMethod(cat);

    // Don't bother if cat has no +load method
    if (!method) return;

    if (PrintLoading) {
        _objc_inform("LOAD: category '%s(%s)' scheduled for +load", 
                     _category_getClassName(cat), _category_getName(cat));
    }
    
    if (loadable_categories_used == loadable_categories_allocated) {
        loadable_categories_allocated = loadable_categories_allocated*2 + 16;
        loadable_categories = (struct loadable_category *)
            realloc(loadable_categories,
                              loadable_categories_allocated *
                              sizeof(struct loadable_category));
    }

    loadable_categories[loadable_categories_used].cat = cat;
    loadable_categories[loadable_categories_used].method = method;
    loadable_categories_used++;
}

以及靜態(tài)變量:

// List of categories that need +load called (pending parent class +load)
static struct loadable_category *loadable_categories = nil;
static int loadable_categories_used = 0;
static int loadable_categories_allocated = 0;

總結(jié)

如果后面的調(diào)用方法和按我們這里加載進(jìn)類列表的順序一致的話,我們可以得出如下結(jié)論:

調(diào)用的大概順序是:比如類 A 和 類 A 的父類都實現(xiàn)了 load 方法拌蜘,甚至還有其 category 都實現(xiàn)了杆烁,那順序是先調(diào)用 A 的父類的 load 方法,再調(diào)用他的或者他自己的 category 的 load 方法简卧。

這可能就是 load 方法調(diào)用時機(jī)問題连躏。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市贞滨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拍棕,老刑警劉巖晓铆,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绰播,居然都是意外死亡骄噪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門蠢箩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來链蕊,“玉大人,你說我怎么就攤上這事谬泌√显希” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵掌实,是天一觀的道長陪蜻。 經(jīng)常有香客問我,道長贱鼻,這世上最難降的妖魔是什么宴卖? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮邻悬,結(jié)果婚禮上症昏,老公的妹妹穿的比我還像新娘。我一直安慰自己父丰,他們只是感情好肝谭,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般分苇。 火紅的嫁衣襯著肌膚如雪添诉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天医寿,我揣著相機(jī)與錄音栏赴,去河邊找鬼。 笑死靖秩,一個胖子當(dāng)著我的面吹牛须眷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沟突,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼花颗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了惠拭?” 一聲冷哼從身側(cè)響起扩劝,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎职辅,沒想到半個月后棒呛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡域携,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年簇秒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秀鞭。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡趋观,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锋边,到底是詐尸還是另有隱情皱坛,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布豆巨,位于F島的核電站麸恍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏搀矫。R本人自食惡果不足惜抹沪,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瓤球。 院中可真熱鬧融欧,春花似錦、人聲如沸卦羡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至欠肾,卻和暖如春瓶颠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刺桃。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工粹淋, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瑟慈。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓桃移,卻偏偏與公主長得像,于是被迫代替她去往敵國和親葛碧。 傳聞我的和親對象是個殘疾皇子借杰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355