iOS探尋+(void)load方法的執(zhí)行時(shí)機(jī)

原文鏈接:https://juejin.cn/post/6844903902710759431

load方法中能實(shí)現(xiàn)什么?

我們經(jīng)常會(huì)有一些需求:讓某方法的執(zhí)行先于main方法。比如要hook某個(gè)系統(tǒng)類的方法(hook UIViewController的viewWillAppear和viewDidDisappear通過(guò)AOP的方式實(shí)現(xiàn)埋點(diǎn)功能),或者做一些初始化操作。這時(shí)候我們經(jīng)常首先想到的就是重寫+(void) load {}方法。
為什么要放在load方法中?我們將從objc源碼去探究。

程序的裝載

main.m中的main方法被稱作程序的入口。但其實(shí)在執(zhí)行到main之前系統(tǒng)首先要裝載應(yīng)用程序并完成動(dòng)態(tài)鏈接,再初始化運(yùn)行環(huán)境,然后準(zhǔn)備好調(diào)用參數(shù),調(diào)用main方法來(lái)啟動(dòng)應(yīng)用程序并把執(zhí)行權(quán)限交于我們的代碼。
我們的代碼會(huì)引用到UIKit、Foundation等框架,還有一些沒(méi)有明確import的類庫(kù),比如libSystem、libobjc等??梢酝ㄟ^(guò)otool -L 可執(zhí)行文件名稱來(lái)查看鏈接的動(dòng)態(tài)庫(kù),他們都是dylib格式。
otool -L XXApp.app/XXApp
XXApp.app/XXApp:

    /usr/lib/libbz2.1.0.dylib (compatibility version 1.0.0, current version 1.0.5)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
    /usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
    /usr/lib/libicucore.A.dylib (compatibility version 1.0.0, current version 62.1.0)
    /usr/lib/libprotobuf.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libsqlite3.dylib (compatibility version 9.0.0, current version 274.22.0)
    /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.11)
    /System/Library/Frameworks/ARKit.framework/ARKit (compatibility version 1.0.0, current version 1.0.0)
    /System/Library/Frameworks/AVFoundation.framework/AVFoundation (compatibility version 1.0.0, current version 2.0.0)

動(dòng)態(tài)鏈接庫(kù)dyld

dyld是動(dòng)態(tài)鏈接器(dynamic link editor),是蘋果的動(dòng)態(tài)連接器。系統(tǒng)先讀取App的可執(zhí)行文件(Mach-O),從里面獲得dyld的路徑,然后加載dyld,然后加載程序相關(guān)依賴庫(kù),對(duì)庫(kù)進(jìn)行鏈接,最后調(diào)用每個(gè)依賴庫(kù)的初始化方法。ojbc的初始化就發(fā)生在這個(gè)時(shí)機(jī)。

objc setup

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?
    environ_init();
    tls_init();
    static_init();
    lock_init();
    exception_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
這里我們可以看到,objc向dyld注冊(cè)了三個(gè)callback函數(shù)。當(dāng)我們的可執(zhí)行文件的image load 后會(huì)執(zhí)行l(wèi)oad_images方法。
load_images(const char *path __unused, const struct mach_header *mh)
{
    // Return without taking locks if there are no +load methods here.
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        mutex_locker_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}

load_images方法中主要做了兩件事情prepare_load_methods,然后call_load_methods。其實(shí)就是準(zhǔn)備好load方法,然后調(diào)用(call)load方法。
我們接著查看call_load_methods方法的實(shí)現(xiàn),在do { } while中,首先調(diào)用class_loads,然后再調(diào)用category_loads。

void call_load_methods(void)
{
    static bool loading = NO;
    bool more_categories;

    loadMethodLock.assertLocked();

    // Re-entrant calls do nothing; the outermost call will finish the job.
    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();

    do {
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}

load_images的執(zhí)行發(fā)生在main方法之前,所以可確保時(shí)機(jī)的要求。
并且我們看到,class中的load方法先執(zhí)行,然后才會(huì)再執(zhí)行category中重寫的```load``方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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