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

在上一篇文章中,我們知道了dyld是蘋果的動(dòng)態(tài)鏈接器,以及講到了鏈接鏡像文件和整個(gè)的加載流程。 那么dyld是怎么和objc進(jìn)行關(guān)聯(lián)的呢,這篇文章就來了解一下。

dyld在加載的時(shí)候會(huì)加載objc,加載objc就會(huì)進(jìn)行初始化來到_objc_init。我們先簡(jiǎn)單的來看一下_objc_init里面有些什么

_objc_init
static bool initialized = false;
    if (initialized) return;
    initialized = true;

這一部分代碼就是判斷是否初始化的條件,初始化了就return。

往下走來到我們的environ_init,環(huán)境變量的初始化,里面主要是讀取影響運(yùn)行時(shí)的環(huán)境變量。我們打印出環(huán)境變量幫助,來玩一下。

    for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
        const option_t *opt = &Settings[I];
         _objc_inform("%s: %s", opt->env, opt->help);
         _objc_inform("%s is set", opt->env);
    }
設(shè)置isa的環(huán)境變量

我們可以把OBJC_DISABLE_NONPOINTER_ISA這個(gè)環(huán)境變量設(shè)置成YES,設(shè)置之后non-pointer就為0了,生成都是普通的isa, 不再是non-pointer1優(yōu)化過的isa了。

同時(shí),我們還可以設(shè)置OBJC_PRINT_LOAD_METHODS為YES,設(shè)置完運(yùn)行之后就能打印所有用到load方法的地方

load方法環(huán)境變量設(shè)置
控制臺(tái)打印load方法用到的地方

這樣你想找某個(gè)方法在哪些類里面使用了就非常方便了。還有很多環(huán)境變量可以玩,就不一一列舉了,介紹幾個(gè)常用的環(huán)境變量

常用環(huán)境變量

第一個(gè)函數(shù)清楚之后,下面的函數(shù)簡(jiǎn)單了解一下:

2、tls_init():關(guān)于線程的處理。線程key的綁定 - 比如線程數(shù)據(jù)的析構(gòu)函數(shù)

3、static_init: 運(yùn)行C ++靜態(tài)構(gòu)造函數(shù)。在dyld調(diào)用我們的靜態(tài)構(gòu)造函數(shù)之前,libc 會(huì)調(diào)用_objc_init(),因此我們必須自己做

4、runtime_init: runtime運(yùn)行時(shí)環(huán)境初始化,里面主要是:unattachedCategories,allocatedClasses 后面會(huì)具體展開分析

5、 exception_init():初始化libobjc的異常處理系統(tǒng)

6、cache_init: 緩存條件初始化

7、_imp_implementationWithBlock_init:?jiǎn)?dòng)回調(diào)機(jī)制。通常這不會(huì)做什么,因?yàn)樗械某跏蓟际嵌栊缘?,但是?duì)于某些進(jìn)程,我們會(huì)迫不及待地加trampolines dylib

8:_dyld_objc_notify_register(&map_images, load_images, unmap_image);:這一句就是我們要重點(diǎn)關(guān)注的,接下來進(jìn)入今天的主題

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

我們?cè)趏bjc4-787源碼工程下發(fā)現(xiàn)點(diǎn)不進(jìn)去_dyld_objc_notify_register,看過我上一篇文章的應(yīng)該知道是啥情況,此時(shí)我們需要打開dyld源碼進(jìn)行全局搜索

_dyld_objc_notify_register

_objc_init里面調(diào)用_dyld_objc_notify_register相當(dāng)于調(diào)到了dyld底層里面的_dyld_objc_notify_register,這個(gè)地方進(jìn)行了跨庫調(diào)用。

接下來就好辦了,
dyld里面的參數(shù)mapped相當(dāng)于_objc_init里面的map_images,
dyld里面的參數(shù)init相當(dāng)于_objc_init里面的load_images,
dyld里面的參數(shù)unmapped相當(dāng)于_objc_init里面的unmap_image,

我們?cè)冱c(diǎn)到registerObjCNotifiers里面

registerObjCNotifiers

這里把_objc_init里面的
map_images賦值給了sNotifyObjCMapped
load_images賦值給了sNotifyObjCInit
我們?cè)僬乙幌?code>sNotifyObjCMapped和sNotifyObjCInit

sNotifyObjCMapped
sNotifyObjCInit

由此可見,_objc_init里面的map_imagesload_images在這兩個(gè)地方進(jìn)行了調(diào)用執(zhí)行。這就形成了一個(gè)關(guān)聯(lián),objcdyld之間是相互來往的。

關(guān)聯(lián)的的流程我在上一篇文章中也講到過,接下來再來總結(jié)一下:

1、在dyld::_main()函數(shù)的流程里,初始化主程序表initializeMainExecutable

2、初始化完了之后來到recursiveInitialization,通過回調(diào)notifySingle對(duì)外通知完成狀態(tài)

3、在notifySingle里面的registerObjCNotifiers完成了賦值操作,registerObjCNotifiers是在_dyld_objc_notify_register調(diào)用的

4、_dyld_objc_notify_register又是在objc源碼里面的_objc_init里面調(diào)用的。從而得出在objc里面注冊(cè)回調(diào)函數(shù),在dyld里面觸發(fā)回調(diào)函數(shù)。

下一篇文章預(yù)告

我們來看看map_images里面做了什么,點(diǎn)進(jìn)去

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);
}

再點(diǎn)到map_images_nolock里面

map_images_nolock

來到map_images_nolock里面后找重點(diǎn),找到了594行,如果鏡像數(shù)量大于0,就開始讀我們所有的鏡像文件

_read_images

來到這里之后代碼非常之多, 直接看的話可能會(huì)摸不著頭尾,先用文字進(jìn)行總結(jié)一下,把大致的流程先過一遍,先從整體再到局部:

1、 條件控制進(jìn)行一次的加載

2、 修復(fù)預(yù)編譯階段的 @selector 的混亂問題

3、錯(cuò)誤混亂的類處理

4、修復(fù)重映射一些沒有被鏡像文件加載進(jìn)來的 類

5、 修復(fù)一些消息!

6、 當(dāng)我們類里面有協(xié)議的時(shí)候 : readProtocol

7、 修復(fù)沒有被加載的協(xié)議

8、 分類處理

9、 類的加載處理

10 、沒有被處理的類 優(yōu)化那些被侵犯的類

了解了read_images的整體流程之后,下一篇文章我們將對(duì)里面的class Protocol selector category進(jìn)行展開分析,敬請(qǐng)期待。

iOS 底層原理 文章匯總

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容