iOS函數(shù)低耦合注冊批量調(diào)用,優(yōu)化App啟動

來源

這是在看了美團的美團外賣iOS App冷啟動治理這篇技術(shù)文章時,自己做的筆記。還有一些筆記可以分享,不過都是一些總結(jié),后面會慢慢發(fā)出來。

理論

我們注冊APP啟動項的時候,如果有多個函數(shù)需要在APP啟動時調(diào)用,難道都在AppDelegate中導(dǎo)入頭文件后再調(diào)用,這樣耦合性高,復(fù)用也差。有些業(yè)務(wù)之間的事件依賴關(guān)系,如果直接在代碼中調(diào)用,不僅耦合嚴重,還不方便平臺化。

借助編譯器函數(shù)和事件注冊的基礎(chǔ)組件,可以降低這種情況。借助編譯器函數(shù)在編譯時把數(shù)據(jù)(如函數(shù)指針)寫入可執(zhí)行文件的__DATA段中,運行時再從__DATA段中取出數(shù)據(jù)進行相應(yīng)的操作(調(diào)用函數(shù))。借用__DATA段,能夠覆蓋所有的啟動階段,例如main()之前的階段。

Clang提供了很多的編譯器函數(shù),它們可以完成不同的功能。其中一種是section()函數(shù),section()函數(shù)提供了二進制段的讀寫能力,它可以將一些編譯期就可以確定的常量寫入數(shù)據(jù)段。在具體的實現(xiàn)中,主要分為編譯期和運行時兩個部分。在編譯期,編譯器會將標記了attribute((section()))的數(shù)據(jù)寫到指定的數(shù)據(jù)段,例如寫一個{key(key代表不同的啟動階段), *pointer}對到數(shù)據(jù)段。到運行時,在合適的時間節(jié)點,再根據(jù)key讀取出函數(shù)指針,完成函數(shù)的調(diào)用。

代碼

參考美團的與github上搜到的,表示感謝,感謝分享!

#import <mach-o/getsect.h>
#import <mach-o/dyld.h>
#import <mach-o/loader.h>
#import <dlfcn.h>
#import <mach-o/ldsyms.h>

#ifndef __LP64__
typedef uint32_t MemoryType;
#else
typedef uint64_t MemoryType;
#endif

typedef void ZLLFunction(void);
#pragma mark - 編譯時 寫入 
void ZLLTest(void) {
    printf("調(diào)用了我\n");
}
void * ZLLLoaddd __attribute__((used, section("__DATA, ZLLDATA"))) = ZLLTest;

#pragma mark - 運行時 調(diào)用
void reaadFunc(char *sectionName, const struct mach_header *mhp);
static void dyld_callback(const struct mach_header *mhp, integer_t vmaddr_slide) {
    printf("callback\n");//會執(zhí)行很多次,每個鏡像都會調(diào)用
    reaadFunc("ZLLDATA", mhp);
}

//該函數(shù)會在main()函數(shù)執(zhí)行之前被自動的執(zhí)行。
__attribute__((constructor))
void initProhet () {
    // 在 dyld 加載鏡像時,會執(zhí)行注冊過的回調(diào)函數(shù),調(diào)用這個方法注冊定義的回調(diào)
    _dyld_register_func_for_add_image(dyld_callback);
    //對于每一個已經(jīng)存在的鏡像,當它被動態(tài)鏈接時,都會執(zhí)行回調(diào) void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide),傳入文件的 mach_header 以及一個虛擬內(nèi)存地址 intptr_t。
}
void reaadFunc(char *sectionName, const struct mach_header *mhp) {
    unsigned long size = 0;
#ifndef __LP64__
    MemoryType *memory = getsectiondata(mhp, SEG_DATA, sectionName, &size);
#else
    const struct mach_header_64 *mhp64 = (const struct mach_header_64 *)mhp;
    MemoryType *memory = (MemoryType*)getsectiondata(mhp64, SEG_DATA, sectionName, &size);
#endif
    long counter = size/ sizeof(void *);
    for (int idx = 0; idx < counter; ++idx) {
        ZLLFunction *my = (ZLLFunction *)memory[idx];
        my();
    }
}

另一種取值方式

#ifdef __LP64__
typedef uint64_t MustOverrideValue;
typedef struct section_64 MustOverrideSection;
#define GetSectByNameFromHeader getsectbynamefromheader_64
#else
typedef uint32_t MustOverrideValue;
typedef struct section MustOverrideSection;
#define GetSectByNameFromHeader getsectbynamefromheader
#endif
static void CheckOverrides(void) {
    Dl_info info;
    dladdr((const void *)&CheckOverrides, &info);
    printf("1\n");
    const MustOverrideValue mach_header = (MustOverrideValue)info.dli_fbase;
    const MustOverrideSection *section = GetSectByNameFromHeader((void *)mach_header, "__DATA", "ZLLDATA");
    if (section == NULL) return;
      printf("2\n");
    
        long counter = section->size/ sizeof(void *);
    for (MustOverrideValue addr = section->offset; addr < section->offset + section->size; addr+=sizeof(void *)) {
    //這兒是傳進去的值得指針
        ZLLFunction **my = (ZLLFunction **)(mach_header +addr);
        (*my)();
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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