iOS底層-dyld和objc的關(guān)聯(lián)

前言

在前一篇文章中我們分析了dyld的加載流程,那么它是如何與objc關(guān)聯(lián)起來的呢?
objc4-781官方源碼_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?
    environ_init();
    tls_init();
    static_init();
    runtime_init();
    exception_init();
    cache_init();
    _imp_implementationWithBlock_init();

    // 什么時(shí)候調(diào)用? images 鏡像文件
    // map_images()
    // load_images()
    
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

參數(shù)說明

  • environ_init() : 讀取影響運(yùn)?時(shí)的環(huán)境變量。如果需要,還可以打印環(huán)境變量幫助。
    我們可以點(diǎn)擊進(jìn)入環(huán)境變量初始化 environ_init 方法,將控制環(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);
    }

控制臺(tái)打印日志如下:

···
objc[38640]: OBJC_DISABLE_TAG_OBFUSCATION is set
objc[38640]: OBJC_DISABLE_NONPOINTER_ISA: disable non-pointer isa fields
objc[38640]: OBJC_DISABLE_NONPOINTER_ISA is set
objc[38640]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY: disable safety checks for +initialize after fork
objc[38640]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY is set

我們設(shè)置打印所有加載的文件的相關(guān)的load方法,設(shè)置環(huán)境 OBJC_PRINT_LOAD_METHODS = YES然后再次打印,控制臺(tái)日志如下:


圖1
···
objc[7093]: LOAD: class 'OS_xpc_connection' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_service' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_null' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_bool' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_double' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_pointer' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_date' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_data' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_string' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_uuid' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_fd' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_shmem' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_mach_send' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_array' scheduled for +load
objc[7093]: LOAD: class 'OS_xpc_dictionary' scheduled for +load
···
  • tls_init() 關(guān)于線程key的綁定 - ?如每線程數(shù)據(jù)的析構(gòu)函數(shù)
void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
    pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
    _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
  • static_init() 運(yùn)?C ++靜態(tài)構(gòu)造函數(shù)。在dyld調(diào)?我們的靜態(tài)構(gòu)造函數(shù)之前,libc 會(huì)調(diào)? _objc_init(),
    因此我們必須??做
  • lock_init(): 沒有重寫,采?C++ 的特性
  • exception_init () 初始化libobjc的異常處理系統(tǒng)
  • cache_init() 緩存條件初始化
  • runtime_init() : runtime運(yùn)?時(shí)環(huán)境初始化,??主要
    是:unattachedCategories,allocatedClasses 后?會(huì)分析
  • _imp_implementationWithBlock_init :啟動(dòng)回調(diào)機(jī)制。通常這不會(huì)做什么,因?yàn)樗械某跏蓟际嵌栊缘?,但是?duì)于某些進(jìn)程,我們會(huì)迫不及待地加載trampolines dylib。
    _dyld_objc_notify_register 這個(gè)方法是跨庫執(zhí)行的,在蘋果開源的 dyld 源碼里面可以找到,然后看到調(diào)用了 dyld::registerObjCNotifiers 這個(gè)方法:
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());
        }
    }
}

load_images

接下來我們分析一下 registerObjCNotifiers 方法的第二個(gè)參數(shù) init,(也就是_dyld_objc_notify_register 方法中的 load_images 參數(shù))方法里面有sNotifyObjCInit = init; 這個(gè)賦值語句,接下來我們在 dyld 源碼中全局搜索一下 sNotifyObjCInit ,會(huì)發(fā)現(xiàn)在 notifySingle 方法中有 sNotifyObjCInit 的調(diào)用:

(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());

所以在 ObjC 中 _objc_init 方法里調(diào)用的 _dyld_objc_notify_register 方法終于在 dyld 源碼中找到其真正調(diào)用的地方,終于找到了它們真正的關(guān)聯(lián)關(guān)系了。

map_images

那么 _dyld_objc_notify_register 方法中的 map_images 參數(shù)呢?接下來我們繼續(xù)找 registerObjCNotifiers 方法中的 sNotifyObjCMapped = mapped; 這一賦值語句,在 dyld 源碼中全局搜索一下 sNotifyObjCMapped ,同樣會(huì)發(fā)現(xiàn)在 notifyBatchPartial 方法中有 sNotifyObjCMapped 的調(diào)用:

(*sNotifyObjCMapped)(objcImageCount, paths, mhs);

所以說 ObjC 中 _objc_init 方法的調(diào)用時(shí)離不開 dyld 的,它們之間有著緊密的聯(lián)系。

我們還可以繼續(xù)探索一下 map_images, 進(jìn)入到 objc_781 源碼,全局搜索相關(guān)的函數(shù)調(diào)用 map_images ,我們能進(jìn)入相關(guān)的函數(shù)調(diào)用過程:

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

再進(jìn)入到 map_images_nolock 方法中,我們能發(fā)現(xiàn)其中有很多加載相關(guān)類的信息 _read_images:

if (hCount > 0) {
    _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}

我們知道,map_images 這個(gè)函數(shù)的主要功能就是為了映射相關(guān)的類信息,所以此處才是我們研究的重點(diǎn),接著進(jìn)入到相關(guān)的類方法的定義 _read_images 方法,順著源碼分析我們能得到很多關(guān)于類的信息:

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)化那些被侵犯的類

?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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