本文介紹dyld與objc的關(guān)聯(lián)
按照蘋(píng)果的解釋,dyld加載庫(kù)時(shí),libSystem會(huì)調(diào)用_objc_init
荚醒,用dyld注冊(cè)鏡像程序汪诉。那來(lái)看看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?
//讀取影響運(yùn)行時(shí)的環(huán)境變量岔擂,如果需要病苗,還可以打開(kāi)環(huán)境變量幫助 export OBJC_HRLP = 1
environ_init();
//關(guān)于線程key的綁定,例如線程數(shù)據(jù)的析構(gòu)函數(shù)
tls_init();
//運(yùn)行C++靜態(tài)構(gòu)造函數(shù)挠阁,在dyld調(diào)用我們的靜態(tài)析構(gòu)函數(shù)之前宾肺,libc會(huì)調(diào)用_objc_init(),因此我們必須自己做
static_init();
//runtime運(yùn)行時(shí)環(huán)境初始化,里面主要是unattachedCategories侵俗、allocatedClasses -- 分類初始化
runtime_init();
//初始化libobjc的異常處理系統(tǒng)
exception_init();
//緩存條件初始化
cache_init();
//啟動(dòng)回調(diào)機(jī)制锨用,通常這不會(huì)做什么,因?yàn)樗械某跏蓟际嵌栊缘陌ィ菍?duì)于某些進(jìn)程增拥,我們會(huì)迫不及待地加載trampolines dylib
_imp_implementationWithBlock_init();
/*
_dyld_objc_notify_register -- dyld 注冊(cè)的地方
- 僅供objc運(yùn)行時(shí)使用
- 注冊(cè)處理程序,以便在映射寻歧、取消映射 和初始化objc鏡像文件時(shí)使用掌栅,dyld將使用包含objc_image_info的鏡像文件數(shù)組,回調(diào) mapped 函數(shù)
map_images: dyld將image鏡像文件加載進(jìn)內(nèi)存時(shí)码泛,會(huì)觸發(fā)該函數(shù)
load_images:dyld初始化image會(huì)觸發(fā)該函數(shù)
unmap_image:dyld將image移除時(shí)會(huì)觸發(fā)該函數(shù)
*/
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
-
environ_init();
環(huán)境變量的設(shè)置猾封,可以更好的監(jiān)控程序。小到去除打印日志時(shí)的時(shí)間噪珊,還可以設(shè)置調(diào)用函數(shù)晌缘,檢驗(yàn)?zāi)男╊愂欠裾{(diào)用了固定的函數(shù)(諸如load齐莲,initwithxxx等)就可以在
Edit Scheme
添加OBJC_PRINT_LOAD_METHODS
為YES,就可以打印出實(shí)現(xiàn)load方法的消息磷箕。
dyld_objc_notify_register
這是dyld
注冊(cè)處理程序
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
從dyld
源碼中找到該方法的解釋
//
// Note: only for use by objc runtime
// Register handlers to be called when objc images are mapped, unmapped, and initialized.
// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
// call dlopen() on them to keep them from being unloaded. During the call to _dyld_objc_notify_register(),
// dyld will call the "mapped" function with already loaded objc images. During any later dlopen() call,
// dyld will also call the "mapped" function. Dyld will call the "init" function when dyld would be called
// initializers in that image. This is when objc calls any +load methods in that image.
//
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped);
{
dyld::registerObjCNotifiers(mapped, init, unmapped);
}
注意:僅供objc運(yùn)行時(shí)使用
在objc鏡像映射选酗、未映射和初始化時(shí)調(diào)用,用來(lái)注冊(cè)處理程序岳枷。
dyld將會(huì)通過(guò)一個(gè)包含objc-image-info的鏡像文件的數(shù)組回調(diào)mapped函數(shù)芒填。
那些是dylibs的鏡像將自動(dòng)增加引用計(jì)數(shù),因此objc以防止它們被卸載空繁,將不再需要
對(duì)它們調(diào)用dlopen()氢烘。 在調(diào)用_dyld_objc_notify_register()的過(guò)程中,dyld將使用已加載的objc鏡像調(diào)用“mapped”函數(shù)家厌。 在以后的任何dlopen()調(diào)用期間,dyld也將調(diào)用“mapped”函數(shù)椎工。
當(dāng)調(diào)用dyld時(shí)饭于,dyld將調(diào)用“ init”函數(shù)初始化鏡像。
objc鏡像在調(diào)用任何類方法+load()的時(shí)候,也會(huì)初始化维蒙。
方法中的三個(gè)參數(shù)分別表示的含義如下:
map_images:dyld將image(鏡像文件)加載進(jìn)內(nèi)存時(shí)掰吕,會(huì)觸發(fā)該函數(shù)
load_image:dyld初始化image會(huì)觸發(fā)該函數(shù)
unmap_image:dyld將image移除時(shí),會(huì)觸發(fā)該函數(shù)
- registerObjCNotifiers
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());
}
}
}
在objc中調(diào)用初始化時(shí)objc_init
,會(huì)調(diào)用 _dyld_objc_notify_register(&map_images, load_images, unmap_image);
,在dyld
中其實(shí)是調(diào)用
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped);
{
dyld::registerObjCNotifiers(mapped, init, unmapped);
}
分析registerObjCNotifiers
實(shí)現(xiàn)颅痊,得到了
map_images = mapped = sNotifyObjCMapped
load_images = init = sNotifyObjCInit
unmap_image = unmapped = sNotifyObjCUnmapped
總結(jié):dyld
在執(zhí)行初始化操作時(shí)殖熟,會(huì)調(diào)用_objc_init
,在_objc_init
中會(huì)注冊(cè)一個(gè)通知函數(shù)_dyld_objc_notify_register
,在實(shí)際的調(diào)用時(shí)是調(diào)用到了registerObjCNotifiers
, 將三個(gè)參數(shù)map_images斑响、load_image菱属、unmap_image
傳遞到對(duì)應(yīng)的dyld中的sNotifyObjCMapped 、sNotifyObjCInit舰罚、sNotifyObjCUnmapped
,從而完成了函數(shù)回調(diào)纽门,給dyld完成初始化操作。