OC底層原理11—dyld與objc的關(guān)聯(lián)

iOS--OC底層原理文章匯總

本文介紹dyld與objc的關(guān)聯(lián)

按照蘋(píng)果的解釋,dyld加載庫(kù)時(shí),libSystem會(huì)調(diào)用_objc_init荚醒,用dyld注冊(cè)鏡像程序汪诉。那來(lái)看看Objc中的初始化_objc_init做了什么

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    //讀取影響運(yùn)行時(shí)的環(huán)境變量岔擂,如果需要病苗,還可以打開(kāi)環(huán)境變量幫助 export OBJC_HRLP = 1
    environ_init();
    //關(guān)于線程key的綁定,例如線程數(shù)據(jù)的析構(gòu)函數(shù)
    tls_init();
    //運(yùn)行C++靜態(tài)構(gòu)造函數(shù)挠阁,在dyld調(diào)用我們的靜態(tài)析構(gòu)函數(shù)之前宾肺,libc會(huì)調(diào)用_objc_init(),因此我們必須自己做
    static_init();
    //runtime運(yùn)行時(shí)環(huán)境初始化,里面主要是unattachedCategories侵俗、allocatedClasses -- 分類初始化
    runtime_init();
    //初始化libobjc的異常處理系統(tǒng)
    exception_init();
    //緩存條件初始化
    cache_init();
    //啟動(dòng)回調(diào)機(jī)制锨用,通常這不會(huì)做什么,因?yàn)樗械某跏蓟际嵌栊缘陌ィ菍?duì)于某些進(jìn)程增拥,我們會(huì)迫不及待地加載trampolines dylib
    _imp_implementationWithBlock_init();

    /*
     _dyld_objc_notify_register -- dyld 注冊(cè)的地方
     - 僅供objc運(yùn)行時(shí)使用
     - 注冊(cè)處理程序,以便在映射寻歧、取消映射 和初始化objc鏡像文件時(shí)使用掌栅,dyld將使用包含objc_image_info的鏡像文件數(shù)組,回調(diào) mapped 函數(shù)
     map_images: dyld將image鏡像文件加載進(jìn)內(nèi)存時(shí)码泛,會(huì)觸發(fā)該函數(shù)
     load_images:dyld初始化image會(huì)觸發(fā)該函數(shù)
     unmap_image:dyld將image移除時(shí)會(huì)觸發(fā)該函數(shù)
     */
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}
  • environ_init(); 環(huán)境變量的設(shè)置猾封,可以更好的監(jiān)控程序。小到去除打印日志時(shí)的時(shí)間噪珊,還可以設(shè)置調(diào)用函數(shù)晌缘,檢驗(yàn)?zāi)男╊愂欠裾{(diào)用了固定的函數(shù)(諸如load齐莲,initwithxxx等)就可以在
    Edit Scheme

    添加OBJC_PRINT_LOAD_METHODS 為YES,就可以打印出實(shí)現(xiàn)load方法的消息磷箕。

dyld_objc_notify_register

這是dyld注冊(cè)處理程序

_dyld_objc_notify_register(&map_images, load_images, unmap_image);

dyld源碼中找到該方法的解釋

//
// Note: only for use by objc runtime
// Register handlers to be called when objc images are mapped, unmapped, and initialized.
// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
// call dlopen() on them to keep them from being unloaded.  During the call to _dyld_objc_notify_register(),
// dyld will call the "mapped" function with already loaded objc images.  During any later dlopen() call,
// dyld will also call the "mapped" function.  Dyld will call the "init" function when dyld would be called
// initializers in that image.  This is when objc calls any +load methods in that image.
//
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped);
{
  dyld::registerObjCNotifiers(mapped, init, unmapped);
}

注意:僅供objc運(yùn)行時(shí)使用
在objc鏡像映射选酗、未映射和初始化時(shí)調(diào)用,用來(lái)注冊(cè)處理程序岳枷。
dyld將會(huì)通過(guò)一個(gè)包含objc-image-info的鏡像文件的數(shù)組回調(diào)mapped函數(shù)芒填。
那些是dylibs的鏡像將自動(dòng)增加引用計(jì)數(shù),因此objc以防止它們被卸載空繁,將不再需要
對(duì)它們調(diào)用dlopen()氢烘。 在調(diào)用_dyld_objc_notify_register()的過(guò)程中,dyld將使用已加載的objc鏡像調(diào)用“mapped”函數(shù)家厌。 在以后的任何dlopen()調(diào)用期間,dyld也將調(diào)用“mapped”函數(shù)椎工。
當(dāng)調(diào)用dyld時(shí)饭于,dyld將調(diào)用“ init”函數(shù)初始化鏡像。
objc鏡像在調(diào)用任何類方法+load()的時(shí)候,也會(huì)初始化维蒙。

方法中的三個(gè)參數(shù)分別表示的含義如下:
map_images:dyld將image(鏡像文件)加載進(jìn)內(nèi)存時(shí)掰吕,會(huì)觸發(fā)該函數(shù)
load_image:dyld初始化image會(huì)觸發(fā)該函數(shù)
unmap_image:dyld將image移除時(shí),會(huì)觸發(fā)該函數(shù)


image.png
  • registerObjCNotifiers
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
    // record functions to call
    sNotifyObjCMapped   = mapped;
    sNotifyObjCInit     = init;
    sNotifyObjCUnmapped = unmapped;

    // call 'mapped' function with all images mapped so far
    try {
        notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
    }
    catch (const char* msg) {
        // ignore request to abort during registration
    }

    // <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
    for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
        ImageLoader* image = *it;
        if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
            dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
            (*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
        }
    }
}

在objc中調(diào)用初始化時(shí)objc_init,會(huì)調(diào)用 _dyld_objc_notify_register(&map_images, load_images, unmap_image);,在dyld中其實(shí)是調(diào)用

void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped);
{
  dyld::registerObjCNotifiers(mapped, init, unmapped);
}

分析registerObjCNotifiers實(shí)現(xiàn)颅痊,得到了
map_images = mapped = sNotifyObjCMapped
load_images = init = sNotifyObjCInit
unmap_image = unmapped = sNotifyObjCUnmapped

總結(jié):dyld在執(zhí)行初始化操作時(shí)殖熟,會(huì)調(diào)用_objc_init,在_objc_init中會(huì)注冊(cè)一個(gè)通知函數(shù)_dyld_objc_notify_register,在實(shí)際的調(diào)用時(shí)是調(diào)用到了registerObjCNotifiers, 將三個(gè)參數(shù)map_images斑响、load_image菱属、unmap_image傳遞到對(duì)應(yīng)的dyld中的sNotifyObjCMapped 、sNotifyObjCInit舰罚、sNotifyObjCUnmapped,從而完成了函數(shù)回調(diào)纽门,給dyld完成初始化操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末营罢,一起剝皮案震驚了整個(gè)濱河市赏陵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌饲漾,老刑警劉巖蝙搔,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異考传,居然都是意外死亡吃型,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門僚楞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)败玉,“玉大人敌土,你說(shuō)我怎么就攤上這事≡艘恚” “怎么了返干?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)血淌。 經(jīng)常有香客問(wèn)我矩欠,道長(zhǎng),這世上最難降的妖魔是什么悠夯? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任癌淮,我火速辦了婚禮,結(jié)果婚禮上沦补,老公的妹妹穿的比我還像新娘乳蓄。我一直安慰自己,他們只是感情好夕膀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布虚倒。 她就那樣靜靜地躺著,像睡著了一般产舞。 火紅的嫁衣襯著肌膚如雪魂奥。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天易猫,我揣著相機(jī)與錄音耻煤,去河邊找鬼。 笑死准颓,一個(gè)胖子當(dāng)著我的面吹牛哈蝇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播攘已,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼买鸽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了贯被?” 一聲冷哼從身側(cè)響起眼五,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎彤灶,沒(méi)想到半個(gè)月后看幼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幌陕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年诵姜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搏熄。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棚唆,死狀恐怖暇赤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宵凌,我是刑警寧澤鞋囊,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站瞎惫,受9級(jí)特大地震影響溜腐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瓜喇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一挺益、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乘寒,春花似錦望众、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至始锚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間喳逛,已是汗流浹背瞧捌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留润文,地道東北人姐呐。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像典蝌,于是被迫代替她去往敵國(guó)和親曙砂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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