前言
在前一篇文章中我們分析了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)日志如下:

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