來源
這是在看了美團的美團外賣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)();
}
}