文集:iOS 底層探索之路
前言
- 在啟動(dòng) app 的時(shí)候债朵, dyld 會(huì)對(duì)動(dòng)態(tài)庫(kù)進(jìn)行加載琳猫、鏈接等一系列動(dòng)作咕娄,之后就會(huì)來(lái)到 libobjc.A.dylib 庫(kù)中調(diào)用 _objc_init 對(duì)類進(jìn)行處理,通過(guò) map_images 映射出整個(gè)鏡像文件女气,再通過(guò) read_images 加載鏡像文件,此時(shí)類已經(jīng)加載完成掉缺,那其中類的加載的流程又是怎么樣的呢?之前的篇章探索了OC底層的內(nèi)容局骤,類的屬性攀圈、方法暴凑、協(xié)議都是怎么加載的呢峦甩?接下來(lái)開(kāi)始類的加載dyld和objc關(guān)聯(lián)的分析。
準(zhǔn)備工作
學(xué)習(xí):iOS App 加載流程知識(shí)
-
了解:main函數(shù)之前的系統(tǒng)操作基本流程main函數(shù)之前的流程.png
- 了解: 動(dòng)態(tài)鏈接器
dyld
的加載流程 :- APP是由內(nèi)核引導(dǎo)啟動(dòng)的现喳,kernel內(nèi)核做好所有準(zhǔn)備工作后會(huì)得到線程入口及main入口凯傲,但是線程不會(huì)馬上進(jìn)入main入口,因?yàn)檫€要加載動(dòng)態(tài)鏈接器(dyld)嗦篱,dyld會(huì)將入口點(diǎn)保存下來(lái)冰单,等dyld加載完所有動(dòng)態(tài)鏈接庫(kù)等工作之后,再開(kāi)始執(zhí)行main函數(shù)灸促。
-
系統(tǒng)kernel做好啟動(dòng)程序的初始準(zhǔn)備后诫欠,交給dyld負(fù)責(zé)。dyld接手后浴栽,系統(tǒng)先讀取 App 的可執(zhí)行文件(Mach-O文件)荒叼,從里面獲取dyld的路徑,然后加載dyld典鸡,dyld去初始化運(yùn)行環(huán)境被廓,開(kāi)啟緩存策略,配合 ImageLoader 將二進(jìn)制文件按格式加載到內(nèi)存萝玷,加載程序相關(guān)依賴庫(kù)(其中也包含我們的可執(zhí)行文件)嫁乘,并對(duì)這些庫(kù)進(jìn)行鏈接昆婿,最后調(diào)用每個(gè)依賴庫(kù)的初始化方法,在這一步蜓斧,runtime被初始化仓蛆。當(dāng)所有依賴庫(kù)初始化后,輪到最后一位(程序可執(zhí)行文件)進(jìn)行初始化法精,在這時(shí)runtime會(huì)對(duì)項(xiàng)目中所有類進(jìn)行類結(jié)構(gòu)初始化多律,然后調(diào)用所有的load方法。最后dyld返回main()函數(shù)地址搂蜓,main()函數(shù)被調(diào)用狼荞。基本流程如圖:dyld的基本加載流程 .png
-
系統(tǒng)kernel做好啟動(dòng)程序的初始準(zhǔn)備后诫欠,交給dyld負(fù)責(zé)。dyld接手后浴栽,系統(tǒng)先讀取 App 的可執(zhí)行文件(Mach-O文件)荒叼,從里面獲取dyld的路徑,然后加載dyld典鸡,dyld去初始化運(yùn)行環(huán)境被廓,開(kāi)啟緩存策略,配合 ImageLoader 將二進(jìn)制文件按格式加載到內(nèi)存萝玷,加載程序相關(guān)依賴庫(kù)(其中也包含我們的可執(zhí)行文件)嫁乘,并對(duì)這些庫(kù)進(jìn)行鏈接昆婿,最后調(diào)用每個(gè)依賴庫(kù)的初始化方法,在這一步蜓斧,runtime被初始化仓蛆。當(dāng)所有依賴庫(kù)初始化后,輪到最后一位(程序可執(zhí)行文件)進(jìn)行初始化法精,在這時(shí)runtime會(huì)對(duì)項(xiàng)目中所有類進(jìn)行類結(jié)構(gòu)初始化多律,然后調(diào)用所有的load方法。最后dyld返回main()函數(shù)地址搂蜓,main()函數(shù)被調(diào)用狼荞。基本流程如圖:
- 在main函數(shù)之前有茫茫多的系統(tǒng)操作流程帮碰,這里只是做大概的思路整理相味,但是為了分析類的加載我們首先明確一點(diǎn) main函數(shù)是我們App程序的入口點(diǎn)函數(shù),在App的main函數(shù)之前殉挽,系統(tǒng)會(huì)首先對(duì)App的runtime運(yùn)行環(huán)境丰涉,做了一系列的初始化操作,動(dòng)態(tài)鏈接器dyld調(diào)用runtime入口函數(shù)斯碌,runtime的入口函數(shù)是_objc_init一死,它是在main函數(shù)之前被dyld調(diào)用的。而+load()方法傻唾,則是在main函數(shù)前被_objc_init調(diào)用投慈,所以_objc_init就是我們研究探索類加載的切入點(diǎn)。
一 冠骄、objc_init分析
我們?cè)趇OS App中設(shè)置符號(hào)斷點(diǎn)_objc_init伪煤,則在App啟動(dòng)時(shí)(進(jìn)入main函數(shù)之前),會(huì)進(jìn)入如下調(diào)用堆棧:
objc_init
源碼如下:
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
*啟動(dòng)初始化凛辣。注冊(cè)我們的鏡像通知與dyld抱既。
*在庫(kù)初始化時(shí)間之前由libSystem調(diào)用
**********************************************************************/
// runtime + 類的信息
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// 環(huán)境變量的一些操作
environ_init();
//對(duì)線程池進(jìn)行初始化的
tls_init();
// 系統(tǒng)級(jí)別的 c++ 構(gòu)造函數(shù)調(diào)用
static_init();
//runtime運(yùn)行時(shí)環(huán)境初始化,里面主要是unattachedCategories扁誓、allocatedClasses -- 分類初始化
runtime_init();
//初始化libobjc的異常處理系統(tǒng)
exception_init();
//啟動(dòng)回調(diào)機(jī)制防泵,通常這不會(huì)做什么,因?yàn)樗械某跏蓟际嵌栊缘幕雀遥菍?duì)于某些進(jìn)程捷泞,我們會(huì)迫不及待地加載trampolines dylib
// cache緩存初始化
cache_init();
//啟動(dòng)回調(diào)機(jī)制
_imp_implementationWithBlock_init();
// 注冊(cè)回調(diào)通知 &:引用類型函數(shù) 外面變了跟著變,load_images值類型只需要加載
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
environ_init
:
方法是讀取影響運(yùn)行時(shí)的環(huán)境變量前普,最后內(nèi)部有一段打印環(huán)境變量的代碼裁良。我將它提取出來(lái)景殷,不做判斷谐岁,打印結(jié)果如下。當(dāng)然也可以在終端使用 export OBJC_HELP=1 指令打印環(huán)境變量贱纠;
tls_init
:
主要是對(duì)線程池進(jìn)行初始化的;
static_init
:
運(yùn)行 C++ 靜態(tài)構(gòu)造函數(shù)响蕴,在 dyld 調(diào)用我們自定義構(gòu)造函數(shù)之前谆焊;
runtime_init
:runtime運(yùn)行時(shí)環(huán)境初始化,里面操作是unattachedCategories浦夷、allocatedClasses(表的初始化)
exception_init
:
初始化 libobjc 庫(kù)的異常處理系統(tǒng)辖试,注冊(cè)監(jiān)聽(tīng)異常崩潰的回調(diào),當(dāng)發(fā)生崩潰時(shí)劈狐,就會(huì)來(lái)到 _objc_terminate 函數(shù)里面罐孝;
cache_init
: cache緩存初始化
_imp_implementationWithBlock_init
:?jiǎn)?dòng)回調(diào)機(jī)制,通常這不會(huì)做什么
_dyld_objc_notify_register
肥缔, dyld注冊(cè)回調(diào)通知:僅供objc運(yùn)行時(shí)使用莲兢,注冊(cè)處理程序,以便在映射续膳,取消映射和初始化objc鏡像時(shí)調(diào)用改艇。Dyld 將使用包含objc_image_info的鏡像文件的數(shù)組回調(diào)"mapped"函數(shù)。
接下來(lái)重點(diǎn)分析將dyld和objc關(guān)聯(lián)的_dyld_objc_notify_register
二 坟岔、_dyld_objc_notify_register分析
_dyld_objc_notify_register
首先我們查看源碼:
//
// 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.
//
// 翻譯如下:
//注意:僅供objc運(yùn)行時(shí)使用
//注冊(cè)要映射谒兄,未映射和初始化objc圖像的處理程序。
// Dyld將使用包含objc-image-info節(jié)的圖像數(shù)組回調(diào)“映射”函數(shù)社付。
//這些是dylib的圖像將自動(dòng)增加引用計(jì)數(shù)承疲,因此objc不再需要
//對(duì)它們調(diào)用dlopen()以防止它們被卸載。 在調(diào)用_dyld_objc_notify_register()的過(guò)程中瘦穆,
// dyld將使用已加載的objc圖像調(diào)用“映射”函數(shù)纪隙。 在以后的任何dlopen()調(diào)用期間赊豌,
// dyld也將調(diào)用“映射”函數(shù)扛或。 當(dāng)調(diào)用dyld時(shí),dyld將調(diào)用“ init”函數(shù)
//該圖片中的初始值設(shè)定項(xiàng)碘饼。 這是objc在該圖像中調(diào)用任何+ load方法的時(shí)候熙兔。
//
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped);
根據(jù)注釋,我們可以知道艾恼,共注冊(cè)了三個(gè)事件的回調(diào):
- _dyld_objc_notify_mapped : OC image被加載映射到內(nèi)存(+load()方法在此時(shí)被調(diào)用)
- _dyld_objc_notify_init : OC image被init時(shí)調(diào)用
- _dyld_objc_notify_unmapped : OC image被移除內(nèi)存時(shí)調(diào)用
通過(guò)這個(gè)注釋我們會(huì)發(fā)現(xiàn)住涉,整個(gè) objc 在這個(gè)里面是一個(gè)運(yùn)行時(shí)環(huán)境,運(yùn)行時(shí)環(huán)境去加載所有類的一些信息的時(shí)候钠绍,就會(huì)依賴這個(gè)注冊(cè)函數(shù)的回調(diào)的通知舆声,告訴當(dāng)前的 dyld 的做了哪些事情,你需要哪些環(huán)境來(lái)進(jìn)行彼此的通訊,所以這其實(shí)是一個(gè)跨庫(kù)的通知
媳握,我們?cè)偃?a target="_blank">dyld3源碼中去查找:
在dyldAPIs.cpp 中
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);
}
typedef void (*_dyld_objc_notify_mapped)(unsigned count, const char* const paths[], const struct mach_header* const mh[]);
typedef void (*_dyld_objc_notify_init)(const char* path, const struct mach_header* mh);
typedef void (*_dyld_objc_notify_unmapped)(const char* path, const struct mach_header* mh);
跳轉(zhuǎn)到 registerObjCNotifiers
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped; //&map_images
sNotifyObjCInit = init; // load_images
sNotifyObjCUnmapped = unmapped; //unmap_image
// 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());
}
}
}
(1). 當(dāng)image被dyld加載到內(nèi)存后碱屁,會(huì)調(diào)用回調(diào)_dyld_objc_notify_mapped
。在runtime
中蛾找,對(duì)應(yīng)的函數(shù)是:map_images
:
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);
}
map_images
方法在 image 加載到內(nèi)存的時(shí)候會(huì)觸發(fā)該方法的調(diào)用娩脾。忽略內(nèi)部函數(shù)跳轉(zhuǎn)、打印和操作 hCount 的代碼打毛,最終會(huì)來(lái)到 _read_images
,其核心是用來(lái)讀取Mach-O格式文件的runtime相關(guān)的section信息柿赊,并轉(zhuǎn)化為runtime內(nèi)部的數(shù)據(jù)結(jié)構(gòu)。
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
......
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
......
}
分析_read_images
,由于其源碼太多幻枉,所以只貼上部分代碼:
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
...
// 如果是第一次進(jìn)來(lái)碰声,就會(huì)走 if 下面的代碼
if (!doneOnce){...}
// 將所有SEL都注冊(cè)到哈希表中,是另外一張哈希表
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {...}
ts.log("IMAGE TIMES: fix up selector references");
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
// 從編譯后的類列表中取出所有類熬甫,獲取到的是一個(gè)classref_t類型的指針
for (EACH_HEADER) {...}
ts.log("IMAGE TIMES: discover classes");
// 主要是修復(fù)重映射 - !noClassesRemapped() 在這里為 false奥邮,所以一般走不進(jìn)來(lái)
// 將未映射Class和Super Class重映射,被remap的類都是非懶加載的類
if (!noClassesRemapped()) {...}
ts.log("IMAGE TIMES: remap classes");
#if SUPPORT_FIXUP
// Fix up old objc_msgSend_fixup call sites
// 修復(fù)舊的函數(shù)指針調(diào)用遺留
for (EACH_HEADER){...}
}
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
#endif
bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
// Discover protocols. Fix up protocol refs.
// 遍歷所有協(xié)議列表罗珍,并且將協(xié)議列表加載到Protocol的哈希表中
for (EACH_HEADER){...}
ts.log("IMAGE TIMES: discover protocols");
// 修復(fù)協(xié)議列表引用洽腺,優(yōu)化后的 images 可能是正確的,但是并不確定
for (EACH_HEADER) {...}
ts.log("IMAGE TIMES: fix up @protocol references");
if (didInitialAttachCategories) {...}
ts.log("IMAGE TIMES: discover categories");
// 實(shí)現(xiàn)非懶加載的類覆旱,對(duì)于load方法和靜態(tài)實(shí)例變量
for (EACH_HEADER) {...}
ts.log("IMAGE TIMES: realize non-lazy classes");
// Realize newly-resolved future classes, in case CF manipulates them
// 遍歷 resolvedFutureClasses 數(shù)組蘸朋,實(shí)現(xiàn)懶加載的類
// resolvedFutureClasses 數(shù)組是在第二步的時(shí)候添加懶加載類的
if (resolvedFutureClasses) {...}
ts.log("IMAGE TIMES: realize future classes");
if (DebugNonFragileIvars) {
realizeAllClasses();
}
// Print preoptimization statistics
if (PrintPreopt){...}
...
}
_read_images
大概過(guò)程: 4和9有關(guān)類的處理是我們關(guān)注的重點(diǎn)·1∶條件控制進(jìn)行一次的加載
·2∶修復(fù)預(yù)編譯階段的@selector的混亂問(wèn)題
·3∶錯(cuò)誤混亂的類處理
·4∶修復(fù)重映射一些沒(méi)有被鏡像文件加載進(jìn)來(lái)的類
·5∶修復(fù)一些消息!
·6∶當(dāng)我們類里面有協(xié)議的時(shí)候∶readProtocol
·7∶ 修復(fù)沒(méi)有被加載的協(xié)議
·8∶分類處理
·9∶類的加載處理
·10∶沒(méi)有被處理的類 優(yōu)化那些被侵犯的類
我們研究的核心是類的加載,所以接下來(lái)逐步分析重點(diǎn)內(nèi)容:
(1.1) _read_images
方法首先創(chuàng)建了兩張表用來(lái)存儲(chǔ)類扣唱。
// 如果是第一次進(jìn)來(lái)藕坯,就會(huì)走 if 下面的代碼
if (!doneOnce) {
// 之后就不會(huì)來(lái)了
// 為什么只來(lái)一次呢,因?yàn)榈谝淮芜M(jìn)來(lái)的時(shí)候噪沙,類炼彪,協(xié)議,sel正歼,分類都沒(méi)有
// 需要?jiǎng)?chuàng)建容器來(lái)保存這些東西辐马,這里創(chuàng)建的是兩個(gè)表。
doneOnce = YES;
//... 忽略一些無(wú)關(guān)緊要的代碼
if (DisableTaggedPointers) {
disableTaggedPointers();
}
// TaggedPointer 的優(yōu)化處理
initializeTaggedPointerObfuscator();
// 4/3是 NXMapTable 的負(fù)載因子
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
// 實(shí)例化存儲(chǔ)類的哈希表局义,并根據(jù)當(dāng)前類的數(shù)量做動(dòng)態(tài)擴(kuò)容
// 只要你沒(méi)有在共享緩存的類喜爷,不管實(shí)現(xiàn)或者未實(shí)現(xiàn)都會(huì)在這個(gè)里面
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
// 已經(jīng)被分配的類和元類都會(huì)放在這個(gè)表里
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
}
(1.2)sel_registerNameNoLock
:修復(fù)預(yù)編譯階段的@selector的混亂問(wèn)題分析
// 修復(fù)預(yù)編譯階段的@selector的混亂問(wèn)題
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->hasPreoptimizedSelectors()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
// sel_cname 將 SEL 強(qiáng)轉(zhuǎn)為 char 類型
const char *name = sel_cname(sels[i]);
// 注冊(cè) SEL 的操作
//
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel) {
sels[i] = sel;
}
}
}
}
-
_getObjc2SelectorRefs
:其中_getObjc2SelectorRefs的源碼如下,表示獲取Mach-O
中的靜態(tài)段__objc_selrefs
萄唇,后續(xù)通過(guò)_getObjc2
開(kāi)頭的Mach-O
靜態(tài)段獲取檩帐,都對(duì)應(yīng)不同的section name - 其中
SEL --> sel
并不是簡(jiǎn)單的字符串,是帶地址的字符串 如下所示另萤,sels[i]與sel字符串一致湃密,但是地址不一致,所以需要調(diào)整為一致的。即fix up
// function name content type section name
GETSECT(_getObjc2SelectorRefs, SEL, "__objc_selrefs");
GETSECT(_getObjc2MessageRefs, message_ref_t, "__objc_msgrefs");
GETSECT(_getObjc2ClassRefs, Class, "__objc_classrefs");
GETSECT(_getObjc2SuperRefs, Class, "__objc_superrefs");
GETSECT(_getObjc2ClassList, classref_t const, "__objc_classlist");
GETSECT(_getObjc2NonlazyClassList, classref_t const, "__objc_nlclslist");
GETSECT(_getObjc2CategoryList, category_t * const, "__objc_catlist");
GETSECT(_getObjc2CategoryList2, category_t * const, "__objc_catlist2");
GETSECT(_getObjc2NonlazyCategoryList, category_t * const, "__objc_nlcatlist");
GETSECT(_getObjc2ProtocolList, protocol_t * const, "__objc_protolist");
GETSECT(_getObjc2ProtocolRefs, protocol_t *, "__objc_protorefs");
GETSECT(getLibobjcInitializers, UnsignedInitializer, "__objc_init_func");
(1.3)重點(diǎn):readClass
分析 :跟class有關(guān)了
// Discover classes. Fix up unresolved future classes. Mark bundle classes.
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
// 從編譯后的類列表中取出所有類泛源,獲取到的是一個(gè)classref_t類型的指針
for (EACH_HEADER) {
if (! mustReadClasses(hi, hasDyldRoots)) {
// Image is sufficiently optimized that we need not call readClass()
// 鏡像充分優(yōu)化揍障,我們不需要調(diào)用 readClass()
continue;
}
classref_t const *classlist = _getObjc2ClassList(hi, &count);
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
for (i = 0; i < count; i++) {
//數(shù)組中會(huì)取出OS_dispatch_queue_concurrent、OS_xpc_object俩由、NSRunloop等系統(tǒng)類毒嫡,例如CF、Fundation幻梯、libdispatch中的類兜畸。以及自己創(chuàng)建的類
Class cls = (Class)classlist[I];
// 通過(guò) readClass 函數(shù)獲取處理后的新類,內(nèi)部主要操作 ro 和 rw 結(jié)構(gòu)體
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
// 初始化所有懶加載的類需要的內(nèi)存空間碘梢,并將所有的未來(lái)需要處理的類添加到一個(gè)數(shù)組中
// 現(xiàn)在數(shù)據(jù)沒(méi)有加載到的咬摇,連類都沒(méi)有初始化
if (newCls != cls && newCls) {
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
ts.log("IMAGE TIMES: discover classes");
/***********************************************************************
* readClass
* Read a class and metaclass as written by a compiler.
* Returns the new class pointer. This could be:
* - cls
* - nil (cls has a missing weak-linked superclass)
* - something else (space for this class was reserved by a future class)
*
* Note that all work performed by this function is preflighted by
* mustReadClasses(). Do not change this function without updating that one.
*
* Locking: runtimeLock acquired by map_images or objc_readClassPair
**********************************************************************/
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->mangledName();
const char *LGPersonName = "LGPerson";
if (strcmp(mangledName, LGPersonName)==0) {
printf("%s -readClass!- %s \n",__func__,mangledName);
}
// 如果某個(gè) cls 的 superclass 是 weak-linked 的并且丟失了,則返回YES煞躬。
if (missingWeakSuperclass(cls)) {
// No superclass (probably weak-linked).
// Disavow any knowledge of this subclass.
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
// 添加到重映射表里面肛鹏,映射為 nil
addRemappedClass(cls, nil);
cls->superclass = nil;
return nil;
}
cls->fixupBackwardDeployingStableSwift();
Class replacing = nil;
if (Class newCls = popFutureNamedClass(mangledName)) { //這個(gè)流程暫時(shí)不做處理 可能在未來(lái)某個(gè)階段處理
// This name was previously allocated as a future class.
// Copy objc_class to future class's struct.
// Preserve future's rw data block.
// 對(duì)未來(lái)新的一些類的 ro 和 rw 的特殊處理,一般不會(huì)進(jìn)去
if (newCls->isAnySwift()) {
_objc_fatal("Can't complete future class request for '%s' "
"because the real class is too big.",
cls->nameForLogging());
}
// 將objc_class復(fù)制到future類的結(jié)構(gòu)中
// 保存future 類的 rw
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro();
memcpy(newCls, cls, sizeof(objc_class));
rw->set_ro((class_ro_t *)newCls->data());
newCls->setData(rw);
freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// ASSERT(cls == getClass(name));
ASSERT(getClassExceptSomeSwift(mangledName));
} else {
// 重點(diǎn)----------------------------進(jìn)到這里來(lái)----------------------------
// 重點(diǎn):從mach-o 文件讀取插入到這兩個(gè)表里面恩沛,自此在內(nèi)存中就可以讀到這個(gè)類了
// 將 cls 加入到 gdb_objc_realized_classes 表里面去
addNamedClass(cls, mangledName, replacing);
// 將 cls 插入到 allocatedClasses 表里面去
addClassTableEntry(cls);
}
// for future reference: shared cache never contains MH_BUNDLEs
// 供以后參考:共享緩存從不包含mh_bundle
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
// 到此時(shí)在扰,這個(gè)類在整個(gè)表里就有了,返回
return cls;
}
注:
// 重點(diǎn):從mach-o 文件讀取插入到這兩個(gè)表里面雷客,自此在內(nèi)存中就可以讀到這個(gè)類了
// 將 cls 加入到 gdb_objc_realized_classes 表里面去
addNamedClass(cls, mangledName, replacing)
;
// 將 cls 插入到 allocatedClasses 表里面去
addClassTableEntry(cls)
;
(1.4) remapClassRef :主要是修復(fù)重映射
// 主要是修復(fù)重映射 - !noClassesRemapped() 在這里為 false芒珠,所以一般走不進(jìn)來(lái)
// 將未映射Class和Super Class重映射,被remap的類都是非懶加載的類
if (!noClassesRemapped()) {
for (EACH_HEADER) {
// 重映射Class搅裙,注意是從_getObjc2ClassRefs函數(shù)中取出類的引用
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[I]);
}
// fixme why doesn't test future1 catch the absence of this?
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[I]);
}
}
}
ts.log("IMAGE TIMES: remap classes");
(1.5) fixupMessageRef: 修復(fù)舊的函數(shù)指針調(diào)用遺留
// Fix up old objc_msgSend_fixup call sites
// 修復(fù)舊的函數(shù)指針調(diào)用遺留
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
if (PrintVtables) {
_objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
"call sites in %s", count, hi->fname());
}
for (i = 0; i < count; i++) {
// 內(nèi)部將常用的 alloc皱卓、objc_msgSend 等函數(shù)指針進(jìn)行注冊(cè),并 fix 為新的函數(shù)指針
fixupMessageRef(refs+i);
}
}
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
(1.6) readProtocol :讀取并初始化Protocol
bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
// Discover protocols. Fix up protocol refs.
// 遍歷所有協(xié)議列表部逮,并且將協(xié)議列表加載到Protocol的哈希表中
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
// cls = Protocol類娜汁,所有協(xié)議和對(duì)象的結(jié)構(gòu)體都類似,isa都對(duì)應(yīng)Protocol類
Class cls = (Class)&OBJC_CLASS_$_Protocol;
ASSERT(cls);
// 獲取protocol哈希表
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->hasPreoptimizedProtocols();
if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) {
if (PrintProtocols) {
_objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
hi->fname());
}
continue;
}
bool isBundle = hi->isBundle();
// 從編譯器中讀取并初始化Protocol
protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
ts.log("IMAGE TIMES: discover protocols");
(1.7) fixupMessageRef :修復(fù)協(xié)議列表引用
// 修復(fù)協(xié)議列表引用兄朋,優(yōu)化后的 images 可能是正確的掐禁,但是并不確定
for (EACH_HEADER) {
// 需要注意到是,下面的函數(shù)是 _getObjc2ProtocolRefs蜈漓,和上面的
// At launch time, we know preoptimized image refs are pointing at the
// shared cache definition of a protocol. We can skip the check on
// launch, but have to visit @protocol refs for shared cache images
// loaded later.
if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized())
continue;
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[I]);
}
}
ts.log("IMAGE TIMES: fix up @protocol references");
(1.8)重點(diǎn):realizeClassWithoutSwift
:類的初始化
// 實(shí)現(xiàn)非懶加載的類穆桂,對(duì)于load方法和靜態(tài)實(shí)例變量
for (EACH_HEADER) {
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
// 實(shí)現(xiàn)所有非懶加載的類(實(shí)例化類對(duì)象的一些信息宫盔,例如rw)
// alloc + init的前提是類的存在融虽,就是從image鏡像文件中讀取并實(shí)現(xiàn)完畢之后的
realizeClassWithoutSwift(cls, nil);
}
}
ts.log("IMAGE TIMES: realize non-lazy classes");
初始化類就在這一步,首先將非懶加載類從 Mach-O 里面讀取出來(lái)灼芭,然后通過(guò)realizeClassWithoutSwift 實(shí)例化 rw有额。
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
if (cls->isRealized()) return cls;
// 判斷 cls 是否已經(jīng)初始化,里面是對(duì) data()->flags 的判斷
ASSERT(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
// 驗(yàn)證類不在共享緩存的未刪除部分
auto ro = (const class_ro_t *)cls->data();
// 判斷類是否是未實(shí)現(xiàn)的未來(lái)類
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// 是未來(lái)的類. rw 已經(jīng)被初始化
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
// 修改 flags
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
// 正常的類. 分配可寫的類數(shù)據(jù)。
// 開(kāi)辟 rw 內(nèi)存空間
rw = objc::zalloc<class_rw_t>();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
// 注: rw ro rwe
//
}
// 判斷是否是元類
#if FAST_CACHE_META
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
// Choose an index for this class.
// Sets cls->instancesRequireRawIsa if indexes no more indexes are available
// 設(shè)置cls->instancesRequireRawIsa如果沒(méi)有更多的索引可用
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
// 遞歸調(diào)用巍佑,實(shí)現(xiàn)父類和元類
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// Metaclasses do not need any features from non pointer ISA
// This allows for a faspath for classes in objc_retain/objc_release.
cls->setInstancesRequireRawIsa();
} else {
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
// 禁用一些類和非指針isa
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
// 禁用非指針的 isa
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
// 非指針isa禁用的環(huán)境或應(yīng)用程序SDK版本
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->name, "OS_object"))
{
// 在 hackedDispatch 里 isa 也充當(dāng)虛表指針
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
// 從元類到根元類設(shè)置
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// 在重新映射時(shí)更新父類和元類
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
// 協(xié)調(diào)實(shí)例變量的偏移量/布局茴迁,可能會(huì)重新分配 class_ro_t,更新我們的 ro 變量萤衰。
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
// 如果還沒(méi)有設(shè)置就開(kāi)始設(shè)置 fastInstanceSize堕义。
cls->setInstanceSize(ro->instanceSize);
// 將一些標(biāo)志從 ro 復(fù)制到 rw
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
// 從ro或父類中傳播關(guān)聯(lián)的對(duì)象禁止標(biāo)志
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// 將這個(gè)類連接到它的父類的子類列表,即雙向綁定
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// 整理 cls 的方法列表脆栋、協(xié)議列表和屬性列表倦卖,以及附加任何未完成的類別
methodizeClass(cls, previously);
return cls;
}
(1.8.1) methodizeClass
:方法的序列化
//方法的序列化
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// Install methods and properties that the class implements itself.
// 將 ro 里面的方法附加到 rw 里面去
method_list_t *list = ro->baseMethods();
if (list) {
// 準(zhǔn)備好方法列表
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
if (rwe) rwe->methods.attachLists(&list, 1);
}
// 將 ro 里面的屬性附加到 rw 里面去
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
// 將 ro 里面的協(xié)議附加到 rw 里面去
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
// 根類獲得額外的方法實(shí)現(xiàn),如果它們還沒(méi)有椿争。這些適用于類別替換之前怕膛。
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories. 附加分類
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
// When a class relocates, categories with class methods
// may be registered on the class itself rather than on
// the metaclass. Tell attachToClass to look for those.
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
......
}
對(duì)methodizeClass
這個(gè)過(guò)程中attachLists
方法進(jìn)行解析:
基本上方法、協(xié)議秦踪、屬性都是通過(guò) attachLists 函數(shù)附加到對(duì)應(yīng)的列表上的
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
//計(jì)算數(shù)組中舊lists的大小
uint32_t oldCount = array()->count;
//計(jì)算新的容量大小 = 舊數(shù)據(jù)大小+新數(shù)據(jù)大小
uint32_t newCount = oldCount + addedCount;
//根據(jù)新的容量大小褐捻,開(kāi)辟一個(gè)數(shù)組,類型是 array_t椅邓,通過(guò)array()獲取
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
//設(shè)置數(shù)組大小
array()->count = newCount;
//舊的數(shù)據(jù)從 addedCount 數(shù)組下標(biāo)開(kāi)始 存放舊的lists柠逞,大小為 舊數(shù)據(jù)大小 * 單個(gè)舊list大小
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
//新數(shù)據(jù)從數(shù)組 首位置開(kāi)始存儲(chǔ),存放新的lists景馁,大小為 新數(shù)據(jù)大小 * 單個(gè)list大小
memcpy(
array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];//將list加入mlists的第一個(gè)元素边苹,此時(shí)的list是一個(gè)一維數(shù)組
}
else {
// 1 list -> many lists 有了一個(gè)list,有往里加很多l(xiāng)ist
//新的list就是分類裁僧,來(lái)自LRU的算法思維个束,即最近最少使用
//獲取舊的list
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
//計(jì)算容量和 = 舊list個(gè)數(shù)+新lists的個(gè)數(shù)
uint32_t newCount = oldCount + addedCount;
//開(kāi)辟一個(gè)容量和大小的集合,類型是 array_t聊疲,即創(chuàng)建一個(gè)數(shù)組茬底,放到array中,通過(guò)array()獲取
setArray((array_t *)malloc(array_t::byteSize(newCount)));
//設(shè)置數(shù)組的大小
array()->count = newCount;
//判斷old是否存在获洲,old肯定是存在的阱表,將舊的list放入到數(shù)組的末尾
if (oldList) array()->lists[addedCount] = oldList;
// memcpy(開(kāi)始位置,放什么贡珊,放多大) 是內(nèi)存平移最爬,從數(shù)組起始位置存入新的list
//其中array()->lists 表示首位元素位置
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
從源碼可以得知,插入表主要分為三種情況:
【
情況1:多對(duì)多
】如果當(dāng)前調(diào)用attachLists的list_array_tt二維數(shù)組中有多個(gè)一維數(shù)組
計(jì)算數(shù)組中舊lists的大小
計(jì)算新的容量大小 = 舊數(shù)據(jù)大小+新數(shù)據(jù)大小
根據(jù)新的容量大小门岔,開(kāi)辟一個(gè)數(shù)組爱致,類型是 array_t,通過(guò)array()獲取
設(shè)置數(shù)組大小
舊的數(shù)據(jù)從 addedCount 數(shù)組下標(biāo)開(kāi)始 存放舊的lists寒随,大小為 舊數(shù)據(jù)大小 * 單個(gè)舊list大小糠悯, 即整段平移帮坚,可以簡(jiǎn)單理解為原來(lái)的數(shù)據(jù)移動(dòng)到后面,即指針偏移
新數(shù)據(jù)從數(shù)組 首位置開(kāi)始存儲(chǔ)互艾,存放新的lists试和,大小為 新數(shù)據(jù)大小 * 單個(gè)list大小,可以簡(jiǎn)單理解為越晚加進(jìn)來(lái)纫普,越在前面阅悍,越在前面,調(diào)用時(shí)則優(yōu)先調(diào)用
【
情況2:0對(duì)一
】如果調(diào)用attachLists的list_array_tt二維數(shù)組為空且新增大小數(shù)目為 1
- 直接賦值addedList的第一個(gè)list
【
情況3:一對(duì)多
】如果當(dāng)前調(diào)用attachLists的list_array_tt二維數(shù)組只有一個(gè)一維數(shù)組
獲取舊的list
計(jì)算容量和 = 舊list個(gè)數(shù)+新lists的個(gè)數(shù)
開(kāi)辟一個(gè)容量和大小的集合昨稼,類型是 array_t溉箕,即創(chuàng)建一個(gè)數(shù)組,放到array中悦昵,通過(guò)array()獲取
設(shè)置數(shù)組的大小
判斷old是否存在肴茄,old肯定是存在的,將舊的list放入到數(shù)組的末尾
memcpy(開(kāi)始位置但指,放什么寡痰,放多大) 是內(nèi)存平移,從數(shù)組起始位置開(kāi)始存入新的list棋凳,其中array()->lists 表示首位元素位置
- 針對(duì)情況3拦坠,這里的lists是指分類
這是日常開(kāi)發(fā)中,為什么子類實(shí)現(xiàn)父類方法會(huì)把父類方法覆蓋的原因
同理剩岳,對(duì)于同名方法贞滨,分類方法覆蓋類方法的原因
這個(gè)操作來(lái)自一個(gè)算法思維 LRU即最近最少使用,加這個(gè)newlist的目的是由于要使用這個(gè)newlist中的方法拍棕,這個(gè)newlist對(duì)于用戶的價(jià)值要高晓铆,即優(yōu)先調(diào)用
會(huì)來(lái)到1對(duì)多的原因 ,主要是有分類的添加绰播,即舊的元素在后面骄噪,新的元素在前面 ,究其根本原因主要是優(yōu)先調(diào)用category蠢箩,這也是分類的意義所在
(2). dyld要init image
的時(shí)候链蕊,會(huì)產(chǎn)生_dyld_objc_notify_init
通知。在runime中, 是通過(guò)load_images
方法做回調(diào)響應(yīng)的谬泌。
-
load_images
函數(shù)是對(duì) load 方法的加載和調(diào)用滔韵。
(2.1) load_images
分析
/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
*
* Locking: write-locks runtimeLock and loadMethodLock
**********************************************************************/
extern bool hasLoadMethods(const headerType *mhdr);
extern void prepare_load_methods(const headerType *mhdr);
void
load_images(const char *path __unused, const struct mach_header *mh)
{
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
didInitialAttachCategories = true;
loadAllCategories();
}
//1.判斷鏡像中是否有Load方法,沒(méi)有直接返回
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// 2.查找所有的Load方法
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
// 準(zhǔn)備加載方法
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
// 3.調(diào)用所有Load方法
call_load_methods();
}
(2.2) prepare_load_methods
分析
準(zhǔn)備加載方法
void prepare_load_methods(const headerType *mhdr)
{
size_t count, I;
runtimeLock.assertLocked();
// 獲取非懶加載類列表
classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
// 循環(huán)遍歷去加載非懶加載類的 load 方法到 loadable_classes
// schedule:安排掌实,預(yù)定
schedule_class_load(remapClass(classlist[i]));
}
// 獲取非懶加載分類列表
category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[I];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
// 如果本類沒(méi)有初始化就去初始化
realizeClassWithoutSwift(cls, nil);
ASSERT(cls->ISA()->isRealized());
// 循環(huán)遍歷去加載非懶加載分類的 load 方法到 loadable_categories
// 和非懶加載類差不多陪蜻,就是數(shù)組不一樣
add_category_to_loadable_list(cat);
}
}
(2.2.1) schedule_class_load
分析
static void schedule_class_load(Class cls)
{
if (!cls) return;
ASSERT(cls->isRealized()); // _read_images 必須實(shí)現(xiàn)
if (cls->data()->flags & RW_LOADED) return;
// 常規(guī)操作,遞歸調(diào)用父類加載 load 方法
schedule_class_load(cls->superclass);
// 將 load 方法加載到 loadable_classes
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
- 可以看出潮峦,是先去加載父類的囱皿,然后再加載本類勇婴,之后再去加載分類的 load 方法
(2.2.2)schedule_class_load
分析
/***********************************************************************
* add_class_to_loadable_list
* Class cls has just become connected. Schedule it for +load if
* it implements a +load method.
**********************************************************************/
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
// 獲取 load 方法的 imp
method = cls->getLoadMethod();
// // 如果沒(méi)有 load 方法直接返回
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
// 擴(kuò)容
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
(2.3) call_load_methods
分析
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.
// 保證只調(diào)用一次
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
// do while 循環(huán)調(diào)用 load 方法
do {
// 1.重復(fù)調(diào)用非懶加載類的 load忱嘹,直到?jīng)]有更多的
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2.調(diào)用非懶加載分類的 load 方法嘱腥,和非懶加載類差不多
more_categories = call_category_loads();
// 3. 如果有類或更多未嘗試的類別,則運(yùn)行更多 load
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
(2.3.1) call_class_loads
分析
static void call_class_loads(void)
{
int I;
// Detach current loadable list.
// 取出 loadable_classes
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
// 調(diào)用保存在 loadable_classes 里的 load 方法
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
// 發(fā)送 load 消息
(*load_method)(cls, @selector(load));
}
// 釋放內(nèi)存
if (classes) free(classes);
}
可以看出load 的調(diào)用順序是:父類->本類->分類拘悦。
load_images方法整體調(diào)用過(guò)程
(3). 當(dāng)dyld要將image移除內(nèi)存時(shí)齿兔,會(huì)發(fā)送_dyld_objc_notify_unmapped
通知。在runtime中础米,是用unmap_image
方法來(lái)響應(yīng)的分苇。
unmap_image
是用來(lái)處理將被dyld
取消映射的給定images
。
void
unmap_image(const char *path __unused, const struct mach_header *mh)
{
recursive_mutex_locker_t lock(loadMethodLock);
mutex_locker_t lock2(runtimeLock);
unmap_image_nolock(mh);
}
void
unmap_image_nolock(const struct mach_header *mh)
{
if (PrintImages) {
_objc_inform("IMAGES: processing 1 newly-unmapped image...\n");
}
header_info *hi;
// Find the runtime's header_info struct for the image
// 查找映像的運(yùn)行時(shí) header_info 結(jié)構(gòu)
for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
if (hi->mhdr() == (const headerType *)mh) {
break;
}
}
if (!hi) return;
if (PrintImages) {
_objc_inform("IMAGES: unloading image for %s%s%s\n",
hi->fname(),
hi->mhdr()->filetype == MH_BUNDLE ? " (bundle)" : "",
hi->info()->isReplacement() ? " (replacement)" : "");
}
// 目前只處理 MH_BUNDLE
_unload_image(hi);
// Remove header_info from header list
// 從標(biāo)題列表中刪除 header_info
removeHeader(hi);
free(hi);
}
三 屁桑、總結(jié)
以上分析了_dyld_objc_notify_register
這個(gè)在dyld和runtime中的跨庫(kù)通知医寿,在runtime中,分析了map_images
蘑斧、load_images
靖秩、unmap_image
方法實(shí)現(xiàn)
_read_images
幾個(gè)重要的方法:
readClass
:通過(guò) readClass 函數(shù)獲取處理后的新類,內(nèi)部主要操作 ro 和 rw 結(jié)構(gòu)體realizeClassWithoutSwift
:實(shí)現(xiàn)所有非懶加載的類methodizeClass
:方法的序列化attachLists
:方法竖瘾、協(xié)議沟突、屬性基本上都是通過(guò) 這個(gè)方法 函數(shù)附加到對(duì)應(yīng)的列表上的
總結(jié)1 :map_images
下的_read_images
方法執(zhí)行流程
- 1
.map_images
加載鏡像文件,_read_images
讀取鏡像文件并加載類捕传, 通過(guò)_getObjc2NonlazyClassList
獲取Mach-O
的靜態(tài)段__objc_nlclslist
非懶加載類表剪廉。- 2.第一次調(diào)用
_read_images
的時(shí)候會(huì)去初始化兩張表gdb_objc_realized_classes
和allocatedClasses
進(jìn)行類信息的存儲(chǔ)耗拓。- 3.初始化表之后就是調(diào)用
readClass
是將類插入到allocatedClasses 表
中的。- 4.然后再通過(guò)
remapClassRef
進(jìn)行修復(fù)類的重映射和fixupMessageRef
修復(fù)舊的函數(shù)指針調(diào)用遺留。- 5.類處理完之后就是添加協(xié)議和方法都注冊(cè)到哈希表中姓迅,方便以后對(duì)其進(jìn)行調(diào)用。
- 6.再通過(guò)
realizeClassWithoutSwift
去初始化類它掂,其中是對(duì)類的rw
實(shí)例化莫秆,以及將ro
數(shù)據(jù)賦值到rw
上。
總結(jié)2:load_images
方法執(zhí)行流程
- 1.判斷是否已經(jīng)初始化分類催什,如果沒(méi)有加載所有分類涵亏;
- 2.判斷鏡像中是否有Load方法,沒(méi)有直接返回蒲凶,通過(guò)hasLoadMethods函數(shù)完成气筋;
- 3.查找所有的Load方法,通過(guò)prepare_load_methods函數(shù)完成旋圆;
- 4.調(diào)用所有Load方法宠默,通過(guò)call_load_methods函數(shù)完成。
load_images
是對(duì) load 方法的處理以及調(diào)用灵巧,調(diào)用順序是父類->本類->分類
搀矫,而有多個(gè)分類 load 的時(shí)候是根據(jù)編譯順序
執(zhí)行的
總結(jié)3:unmap_image
方法執(zhí)行流程
unmap_image
是用來(lái)處理將被 dyld 取消映射的給定 images
在本篇文章中抹沪,我們知道dyld在main()函數(shù)之前,會(huì)調(diào)用runtime的_objc_init 方法瓤球。_objc_init是runtime的入口函數(shù)融欧,通過(guò)跨庫(kù)通知_dyld_objc_notify_register
,實(shí)現(xiàn)類的加載卦羡,使dyld和objc關(guān)聯(lián)噪馏。在上文提到過(guò)類的懶加載和非懶加載,下篇文章分析下绿饵,類在懶加載和非懶加載兩種情況下有啥不同欠肾。
四 、拓展:關(guān)于class_ro_t
和 class_rw_t
類的結(jié)構(gòu)以前分析過(guò)的
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
........
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
.........
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
...........
class_ro_t
存儲(chǔ)了當(dāng)前類在編譯期就已經(jīng)確定的屬性拟赊、方法以及遵循的協(xié)議刺桃,里面是沒(méi)有分類的方法的。那些運(yùn)行時(shí)添加的方法將會(huì)存儲(chǔ)在運(yùn)行時(shí)生成的class_rw_t
中吸祟。
ro
即表示read only
瑟慈,是無(wú)法進(jìn)行修改的。ObjC 類中的屬性欢搜、方法還有遵循的協(xié)議等信息都保存在
class_rw_t
中
class_rw_t
生成時(shí)機(jī)
class_rw_t
生成在運(yùn)行時(shí)封豪,在編譯期間,class_ro_t
結(jié)構(gòu)體就已經(jīng)確定炒瘟,objc_class中的bits的data部分存放著該結(jié)構(gòu)體的地址吹埠。在runtime運(yùn)行之后,具體說(shuō)來(lái)是在運(yùn)行runtime的realizeClass
方法時(shí)疮装,會(huì)生成class_rw_t
結(jié)構(gòu)體缘琅,該結(jié)構(gòu)體包含了class_ro_t
,并且更新data部分廓推,換成class_rw_t
結(jié)構(gòu)體的地址刷袍。
區(qū)別:
class_ro_t
存放的是編譯期間就確定的;
class_rw_t
是在runtime時(shí)才確定樊展,它會(huì)先將class_ro_t
的內(nèi)容拷貝過(guò)去呻纹,然后再將當(dāng)前類的分類的這些屬性、方法等拷貝到其中专缠。
所以可以說(shuō)class_rw_t
是class_ro_t
的超集雷酪,當(dāng)然實(shí)際訪問(wèn)類的方法、屬性等也都是訪問(wèn)的class_rw_t
中的內(nèi)容