dyld和objc的關(guān)聯(lián)

一彩掐、objc


查看objc源碼的時候看到了void _objc_init(void)函數(shù)

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    environ_init();
    tls_init();
    static_init();
    runtime_init();
    exception_init();
    cache_init();
    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

那這個方法什么時候進來的呢,我們打一個斷點,通過lldbbt命令看一下堆棧

image.png

結(jié)合這張圖我們得知_objc_init調(diào)用流程大致為:

dyld的doModInitFunctions方法調(diào)用libSystem.B.dylib的libSystem_initializer方法毛仪;接著初始化了libdispatch; libdispatch又調(diào)用了_os_object_int,最終來到了_objc_init

  • 1叛买、這里其實是各種初始化:環(huán)境初始化;靜態(tài)變量初始化飞涂;運行時初始化赶舆;異常初始化踪栋;cache初始化等等
  • 2敛腌、這里最重要的是_dyld_objc_notify_register方法,他這里就是dyld的注冊監(jiān)聽方法卧土,用來跟objc關(guān)聯(lián)的惫皱。

二像樊、dyld和objc關(guān)聯(lián)過程

1、_dyld_objc_notify_register解析

  • 1.1 在objc代碼中我們看到它的聲明
//
// 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);

不知道大家有沒有注意到源碼中的注釋+load()旅敷,這是一個小細節(jié)生棍,初始化鏡像的時候會調(diào)用在鏡像中的+load()方法.

  • 1.2 在這個注釋中說dyld將會調(diào)用mappedunmapped媳谁、initialized這些方法涂滴;既然是dyld調(diào)用那這些方法就應(yīng)該會傳到dyld中啊友酱,順著這條思路應(yīng)該能想到_dyld_objc_notify_register的實現(xiàn)應(yīng)該在dyld中,果不其然,在dyld3
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped)
{
    log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped, init, unmapped);

    gAllImages.setObjCNotifiers(mapped, init, unmapped);
}

2、setObjCNotifiers解析

void AllImages::setObjCNotifiers(_dyld_objc_notify_mapped map, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmap)
{
    _objcNotifyMapped   = map;
    _objcNotifyInit     = init;
    _objcNotifyUnmapped = unmap;
   //.....此處省略
}

這三個函數(shù)指針會賦值給_objcNotifyMapped柔纵、_objcNotifyInit缔杉、_objcNotifyUnmapped這三個變量,其實這里就是寫入注冊函數(shù)

3、map_images解析

void
map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    mutex_locker_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}

接著進入map_images_nolock函數(shù)看看搁料,這里的核心代碼是_read_images方法

    if (hCount > 0) {
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
  • 3.1: 條件控制進行一次的加載
  • 3.2: 修復(fù)預(yù)編譯階段的 @selector 的混亂問題
  • 3.3: 查找類或详,錯誤混亂的類處理
  • 3.4: 修復(fù)重映射一些沒有被鏡像文件加載進來的 類
  • 3.5: 修復(fù)一些消息!
  • 3.6: 當我們類里面有協(xié)議的時候 : readProtocol
  • 3.7: 修復(fù)沒有被加載的協(xié)議
  • 3.8: 分類處理
  • 3.9: 類的加載處理
  • 3.10: 沒有被處理的類 優(yōu)化那些被侵犯的

4、readClass解析
_read_images中有一個readClass方法的調(diào)用;

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    const char *mangledName = cls->mangledName();
    
    if (missingWeakSuperclass(cls)) {
        // No superclass (probably weak-linked). 
        // Disavow any knowledge of this subclass.
        if (PrintConnecting) {
            _objc_inform("CLASS: IGNORING class '%s' with "
                         "missing weak-linked superclass", 
                         cls->nameForLogging());
        }
        addRemappedClass(cls, nil);
        cls->superclass = nil;
        return nil;
    }
    
    cls->fixupBackwardDeployingStableSwift();

    Class replacing = nil;
    if (Class newCls = popFutureNamedClass(mangledName)) {
        // This name was previously allocated as a future class.
        // Copy objc_class to future class's struct.
        // Preserve future's rw data block.
        
        if (newCls->isAnySwift()) {
            _objc_fatal("Can't complete future class request for '%s' "
                        "because the real class is too big.", 
                        cls->nameForLogging());
        }
        
        class_rw_t *rw = newCls->data();
        const class_ro_t *old_ro = rw->ro();
        memcpy(newCls, cls, sizeof(objc_class));
        rw->set_ro((class_ro_t *)newCls->data());
        newCls->setData(rw);
        freeIfMutable((char *)old_ro->name);
        free((void *)old_ro);
        
        addRemappedClass(cls, newCls);
        
        replacing = cls;
        cls = newCls;
    }
    
    if (headerIsPreoptimized  &&  !replacing) {
        // class list built in shared cache
        // fixme strict assert doesn't work because of duplicates
        // ASSERT(cls == getClass(name));
        ASSERT(getClassExceptSomeSwift(mangledName));
    } else {
        addNamedClass(cls, mangledName, replacing);
        addClassTableEntry(cls);
    }

    // for future reference: shared cache never contains MH_BUNDLEs
    if (headerIsBundle) {
        cls->data()->flags |= RO_FROM_BUNDLE;
        cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
    }
    
    return cls;
}

這一步會把class信息從二進制里面讀出來郭计,

  • newCls->data()取出來作rw 霸琴;
  • newCls->data()再取出來強轉(zhuǎn)為class_ro_t *放到到rwro部分
  • addClassTableEntry這是將類插入到類的集合表中,為了后面調(diào)用的快速查找

三昭伸、總結(jié)

dyld和objc中是存在著一些交互的過程梧乘,需要關(guān)聯(lián)起來進行研究學(xué)習(xí)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庐杨,一起剝皮案震驚了整個濱河市选调,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辑莫,老刑警劉巖学歧,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異各吨,居然都是意外死亡枝笨,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門揭蜒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來横浑,“玉大人,你說我怎么就攤上這事屉更♂闳冢” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵瑰谜,是天一觀的道長欺冀。 經(jīng)常有香客問我,道長萨脑,這世上最難降的妖魔是什么隐轩? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮渤早,結(jié)果婚禮上职车,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好悴灵,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布扛芽。 她就那樣靜靜地躺著,像睡著了一般积瞒。 火紅的嫁衣襯著肌膚如雪川尖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天茫孔,我揣著相機與錄音空厌,去河邊找鬼。 笑死银酬,一個胖子當著我的面吹牛嘲更,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播揩瞪,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼赋朦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了李破?” 一聲冷哼從身側(cè)響起宠哄,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嗤攻,沒想到半個月后毛嫉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡妇菱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年承粤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闯团。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡辛臊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出房交,到底是詐尸還是另有隱情彻舰,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布候味,位于F島的核電站刃唤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏白群。R本人自食惡果不足惜尚胞,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望川抡。 院中可真熱鬧辐真,春花似錦、人聲如沸崖堤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽密幔。三九已至楔脯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胯甩,已是汗流浹背昧廷。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留偎箫,地道東北人木柬。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像淹办,于是被迫代替她去往敵國和親眉枕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354