該文章屬于劉小壯原創(chuàng),轉(zhuǎn)載請(qǐng)注明:劉小壯

程序加載過程
在iOS程序中會(huì)用到很多系統(tǒng)的動(dòng)態(tài)庫,這些動(dòng)態(tài)庫都是動(dòng)態(tài)加載的。所有iOS程序共用一套系統(tǒng)動(dòng)態(tài)庫,在程序開始運(yùn)行時(shí)才會(huì)開始鏈接動(dòng)態(tài)庫。

除了在項(xiàng)目設(shè)置里顯式出現(xiàn)的動(dòng)態(tài)庫外,還會(huì)有一些隱式存在的動(dòng)態(tài)庫。例如objc和Runtime所屬的libobjc.dyld和libSystem.dyld,在libSystem中包含常用的libdispatch(GCD)、libsystem_c(C語言基礎(chǔ)庫)、libsystem_blocks(Block)等。
使用動(dòng)態(tài)庫的優(yōu)點(diǎn):
- 防止重復(fù)。
iOS系統(tǒng)中所有App公用一套系統(tǒng)動(dòng)態(tài)庫,防止重復(fù)的內(nèi)存占用。 - 減少包體積。因?yàn)橄到y(tǒng)動(dòng)態(tài)庫被內(nèi)置到
iOS系統(tǒng)中,所以打包時(shí)不需要把這部分代碼打進(jìn)去,可以減小包體積。 - 動(dòng)態(tài)性。因?yàn)橄到y(tǒng)動(dòng)態(tài)庫是動(dòng)態(tài)加載的,所以可以在更新系統(tǒng)后,將動(dòng)態(tài)庫換成新的動(dòng)態(tài)庫。
加載過程
在應(yīng)用程序啟動(dòng)后,由dyld(the dynamic link editor)進(jìn)行程序的初始化操作。大概流程就像下面列出的步驟,其中第3、4、5步會(huì)執(zhí)行多次,在ImageLoader加載新的image進(jìn)內(nèi)存后就會(huì)執(zhí)行一次。
- 在引用程序啟動(dòng)后,由
dyld將應(yīng)用程序加載到二進(jìn)制中,并完成一些文件的初始化操作。 -
Runtime向dyld中注冊(cè)回調(diào)函數(shù)。 - 通過
ImageLoader將所有image加載到內(nèi)存中。 -
dyld在image發(fā)生改變時(shí),主動(dòng)調(diào)用回調(diào)函數(shù)。 -
Runtime接收到dyld的函數(shù)回調(diào),開始執(zhí)行map_images、load_images等操作,并回調(diào)+load方法。 - 調(diào)用
main()函數(shù),開始執(zhí)行業(yè)務(wù)代碼。
ImageLoader是image的加載器,image可以理解為編譯后的二進(jìn)制。
下面是在Runtime的map_images函數(shù)打斷點(diǎn),觀察回調(diào)情況的匯編代碼。可以看出,調(diào)用是由dyld發(fā)起的,由ImageLoader通知dyld進(jìn)行調(diào)用。

關(guān)于dyld我并沒有深入研究,有興趣的同學(xué)可以到Github上下載源碼研究一下。
動(dòng)態(tài)加載
一個(gè)OC程序可以在運(yùn)行過程中動(dòng)態(tài)加載和鏈接新類或Category,新類或Category會(huì)加載到程序中,其處理方式和其他類是相同的。動(dòng)態(tài)加載還可以做許多不同的事,動(dòng)態(tài)加載允許應(yīng)用程序進(jìn)行自定義處理。
OC提供了objc_loadModules運(yùn)行時(shí)函數(shù),執(zhí)行Mach-O中模塊的動(dòng)態(tài)加載,在上層NSBundle對(duì)象提供了更簡單的訪問API。
map images
在Runtime加載時(shí),會(huì)調(diào)用_objc_init函數(shù),并在內(nèi)部注冊(cè)三個(gè)函數(shù)指針。其中map_images函數(shù)是初始化的關(guān)鍵,內(nèi)部完成了大量Runtime環(huán)境的初始化操作。
在map_images函數(shù)中,內(nèi)部也是做了一個(gè)調(diào)用中轉(zhuǎn)。然后調(diào)用到map_images_nolock函數(shù),內(nèi)部核心就是_read_images函數(shù)。
void _objc_init(void)
{
// .... 各種init
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
void map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
rwlock_writer_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
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函數(shù)中完成了大量的初始化操作,函數(shù)內(nèi)部代碼量比較大,下面是精簡版帶注釋的源代碼。
先整體梳理一遍_read_images函數(shù)內(nèi)部的邏輯:
- 加載所有類到類的
gdb_objc_realized_classes表中。 - 對(duì)所有類做重映射。
- 將所有
SEL都注冊(cè)到namedSelectors表中。 - 修復(fù)函數(shù)指針遺留。
- 將所有
Protocol都添加到protocol_map表中。 - 對(duì)所有
Protocol做重映射。 - 初始化所有非懶加載的類,進(jìn)行
rw、ro等操作。 - 遍歷已標(biāo)記的懶加載的類,并做初始化操作。
- 處理所有
Category,包括Class和Meta Class。 - 初始化所有未初始化的類。
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
header_info *hi;
uint32_t hIndex;
size_t count;
size_t i;
Class *resolvedFutureClasses = nil;
size_t resolvedFutureClassCount = 0;
static bool doneOnce;
TimeLogger ts(PrintImageTimes);
#define EACH_HEADER \
hIndex = 0; \
hIndex < hCount && (hi = hList[hIndex]); \
hIndex++
if (!doneOnce) {
doneOnce = YES;
// 實(shí)例化存儲(chǔ)類的哈希表,并且根據(jù)當(dāng)前類數(shù)量做動(dòng)態(tài)擴(kuò)容
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
}
// 由編譯器讀取類列表,并將所有類添加到類的哈希表中,并且標(biāo)記懶加載的類并初始化內(nèi)存空間
for (EACH_HEADER) {
if (! mustReadClasses(hi)) {
continue;
}
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->isPreoptimized();
/** 將新類添加到哈希表中 */
// 從編譯后的類列表中取出所有類,獲取到的是一個(gè)classref_t類型的指針
classref_t *classlist = _getObjc2ClassList(hi, &count);
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];
// 通過readClass函數(shù)獲取處理后的新類,內(nèi)部主要操作ro和rw結(jié)構(gòu)體
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
// 初始化所有懶加載的類需要的內(nèi)存空間
if (newCls != cls && newCls) {
// 將懶加載的類添加到數(shù)組中
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
// 將未映射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]);
}
// 重映射父類
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
// 將所有SEL都注冊(cè)到哈希表中,是另外一張哈希表
static size_t UnfixedSelectors;
sel_lock();
for (EACH_HEADER) {
if (hi->isPreoptimized()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
// 注冊(cè)SEL的操作
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
// 修復(fù)舊的函數(shù)指針調(diào)用遺留
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
for (i = 0; i < count; i++) {
// 內(nèi)部將常用的alloc、objc_msgSend等函數(shù)指針進(jìn)行注冊(cè),并fix為新的函數(shù)指針
fixupMessageRef(refs+i);
}
}
// 遍歷所有協(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->isPreoptimized();
bool isBundle = hi->isBundle();
// 從編譯器中讀取并初始化Protocol
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
// 修復(fù)協(xié)議列表引用,優(yōu)化后的images可能是正確的,但是并不確定
for (EACH_HEADER) {
// 需要注意到是,下面的函數(shù)是_getObjc2ProtocolRefs,和上面的_getObjc2ProtocolList不一樣
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
// 實(shí)現(xiàn)非懶加載的類,對(duì)于load方法和靜態(tài)實(shí)例變量
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
// 實(shí)現(xiàn)所有非懶加載的類(實(shí)例化類對(duì)象的一些信息,例如rw)
realizeClass(cls);
}
}
// 遍歷resolvedFutureClasses數(shù)組,實(shí)現(xiàn)所有懶加載的類
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
// 實(shí)現(xiàn)懶加載的類
realizeClass(resolvedFutureClasses[i]);
resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
// 發(fā)現(xiàn)和處理所有Category
for (EACH_HEADER) {
// 外部循環(huán)遍歷找到當(dāng)前類,查找類對(duì)應(yīng)的Category數(shù)組
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
// 內(nèi)部循環(huán)遍歷當(dāng)前類的所有Category
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
// 首先,通過其所屬的類注冊(cè)Category。如果這個(gè)類已經(jīng)被實(shí)現(xiàn),則重新構(gòu)造類的方法列表。
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
// 將Category添加到對(duì)應(yīng)Class的value中,value是Class對(duì)應(yīng)的所有category數(shù)組
addUnattachedCategoryForClass(cat, cls, hi);
// 將Category的method、protocol、property添加到Class
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
}
// 這塊和上面邏輯一樣,區(qū)別在于這塊是對(duì)Meta Class做操作,而上面則是對(duì)Class做操作
// 根據(jù)下面的邏輯,從代碼的角度來說,是可以對(duì)原類添加Category的
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
}
}
}
// 初始化從磁盤中加載的所有類,發(fā)現(xiàn)Category必須是最后執(zhí)行的
// 從runtime objc4-532版本源碼來看,DebugNonFragileIvars字段一直是-1,所以不會(huì)進(jìn)入這個(gè)方法中
if (DebugNonFragileIvars) {
realizeAllClasses();
}
#undef EACH_HEADER
}
其內(nèi)部還調(diào)用了很多其他函數(shù),后面會(huì)詳細(xì)介紹函數(shù)內(nèi)部實(shí)現(xiàn)。
load images
在項(xiàng)目中經(jīng)常用到load類方法,load類方法的調(diào)用時(shí)機(jī)比main函數(shù)還要靠前。load方法是由系統(tǒng)來調(diào)用的,并且在整個(gè)程序運(yùn)行期間,只會(huì)調(diào)用一次,所以可以在load方法中執(zhí)行一些只執(zhí)行一次的操作。
一般Method Swizzling都會(huì)放在load方法中執(zhí)行,這樣在執(zhí)行main函數(shù)前,就可以對(duì)類方法進(jìn)行交換??梢源_保正式執(zhí)行代碼時(shí),方法肯定是被交換過的。
如果對(duì)一個(gè)類添加Category后,并且重寫其原有方法,這樣會(huì)導(dǎo)致Category中的方法覆蓋原類的方法。但是load方法卻是例外,所有Category和原類的load方法都會(huì)被執(zhí)行。
源碼分析
load方法由Runtime進(jìn)行調(diào)用,下面我們分析一下load方法的實(shí)現(xiàn),load的實(shí)現(xiàn)源碼都在objc-loadmethod.mm中。
在一個(gè)新的工程中,我們創(chuàng)建一個(gè)TestObject類,并在其load方法中打一個(gè)斷點(diǎn),看一下系統(tǒng)的調(diào)用堆棧。

從調(diào)用??梢钥闯?,是通過系統(tǒng)的動(dòng)態(tài)鏈接器dyld開始的調(diào)用,然后調(diào)到Runtime的load_images函數(shù)中。load_images函數(shù)是通過_dyld_objc_notify_register函數(shù),將自己的函數(shù)指針注冊(cè)給dyld的。
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();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
在load_images函數(shù)中主要做了兩件事,首先通過prepare_load_methods函數(shù)準(zhǔn)備Class load list和Category load list,然后通過call_load_methods函數(shù)調(diào)用已經(jīng)準(zhǔn)備好的兩個(gè)方法列表。
void
load_images(const char *path __unused, const struct mach_header *mh)
{
if (!hasLoadMethods((const headerType *)mh)) return;
prepare_load_methods((const headerType *)mh);
call_load_methods();
}
首先我們看一下prepare_load_methods函數(shù)的實(shí)現(xiàn),看一下其內(nèi)部是怎么查找load方法的??梢钥吹剑鋬?nèi)部主要分為兩部分,查找Class的load方法列表和查找Category方法列表。
準(zhǔn)備類的方法列表時(shí),首先通過_getObjc2NonlazyClassList函數(shù)獲取所有非懶加載類的列表,這時(shí)候獲取到的是一個(gè)classref_t類型的數(shù)組,然后遍歷數(shù)組添加load方法列表。
Category過程也是類似,通過_getObjc2NonlazyCategoryList函數(shù)獲取所有非懶加載Category的列表,得到一個(gè)category_t類型的數(shù)組,需要注意的是這是一個(gè)指向指針的指針。然后對(duì)其進(jìn)行遍歷并添加到load方法列表,Class和Category的load方法列表是兩個(gè)列表。
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
// 獲取到非懶加載的類的列表
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
// 設(shè)置Class的調(diào)用列表
schedule_class_load(remapClass(classlist[i]));
}
// 獲取到非懶加載的Category列表
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
// 忽略弱鏈接的類別
if (!cls) continue;
// 實(shí)例化所屬的類
realizeClass(cls);
// 設(shè)置Category的調(diào)用列表
add_category_to_loadable_list(cat);
}
}
在添加類的load方法列表時(shí),內(nèi)部會(huì)遞歸遍歷把所有父類的load方法都添加進(jìn)去,順著繼承者鏈的順序添加,會(huì)先把父類添加在前面。然后會(huì)調(diào)用add_class_to_loadable_list函數(shù),將自己的load方法添加到對(duì)應(yīng)的數(shù)組中。
static void schedule_class_load(Class cls)
{
if (!cls) return;
// 已經(jīng)添加Class的load方法到調(diào)用列表中
if (cls->data()->flags & RW_LOADED) return;
// 確保super已經(jīng)被添加到load列表中,默認(rèn)是整個(gè)繼承者鏈的順序
schedule_class_load(cls->superclass);
// 將IMP和Class添加到調(diào)用列表
add_class_to_loadable_list(cls);
// 設(shè)置Class的flags,表示已經(jīng)添加Class到調(diào)用列表中
cls->setInfo(RW_LOADED);
}
而Category則不需要考慮父類的問題,所以直接在prepare_load_methods函數(shù)中遍歷Category數(shù)組,然后調(diào)用add_category_to_loadable_list函數(shù)即可。
在add_category_to_loadable_list函數(shù)中,會(huì)判斷當(dāng)前Category有沒有實(shí)現(xiàn)load方法,如果沒有則直接return,如果實(shí)現(xiàn)了則添加到loadable_categories數(shù)組中。
類的add_class_to_loadable_list函數(shù)內(nèi)部實(shí)現(xiàn)也是類似,區(qū)別在于類的數(shù)組叫做loadable_classes。
void add_category_to_loadable_list(Category cat)
{
IMP method;
// 獲取Category的load方法的IMP
method = _category_getLoadMethod(cat);
// 如果Category沒有l(wèi)oad方法則return
if (!method) return;
// 如果已使用大小等于數(shù)組大小,對(duì)數(shù)組進(jìn)行動(dòng)態(tài)擴(kuò)容
if (loadable_categories_used == loadable_categories_allocated) {
loadable_categories_allocated = loadable_categories_allocated*2 + 16;
loadable_categories = (struct loadable_category *)
realloc(loadable_categories,
loadable_categories_allocated *
sizeof(struct loadable_category));
}
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
到此為止,loadable_classes和loadable_categories兩個(gè)數(shù)組已經(jīng)準(zhǔn)備好了,load_images會(huì)調(diào)用call_load_methods函數(shù)執(zhí)行這些load方法。在這個(gè)方法中,call_class_loads函數(shù)是負(fù)責(zé)調(diào)用類方法列表的,call_category_loads負(fù)責(zé)調(diào)用Category的方法列表。
void call_load_methods(void)
{
bool more_categories;
void *pool = objc_autoreleasePoolPush();
do {
// 反復(fù)執(zhí)行call_class_loads函數(shù),直到數(shù)組中沒有可執(zhí)行的load方法
while (loadable_classes_used > 0) {
call_class_loads();
}
more_categories = call_category_loads();
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
下面是調(diào)用類方法列表的代碼,內(nèi)部主要是通過對(duì)loadable_classes數(shù)組進(jìn)行遍歷,并獲取到loadable_class的結(jié)構(gòu)體,結(jié)構(gòu)體中存在Class和IMP,然后直接調(diào)用即可。
Category的調(diào)用方式和類的一樣,就不在下面貼代碼了。需要注意的是,load方法都是直接調(diào)用的,并沒有走運(yùn)行時(shí)的objc_msgSend函數(shù)。
static void call_class_loads(void)
{
int i;
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
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;
(*load_method)(cls, SEL_load);
}
if (classes) free(classes);
}
struct loadable_class {
Class cls; // may be nil
IMP method;
};
根據(jù)上面的源碼分析,我們可以看出load方法的調(diào)用順序應(yīng)該是父類 -> 子類 -> 分類的順序。因?yàn)閳?zhí)行加載Class的時(shí)機(jī)是在Category之前的,而且load子類之前會(huì)先load父類,所以是這種順序。
initialize
和load方法類似的也有initialize方法,initialize方法也是由Runtime進(jìn)行調(diào)用的,自己不可以直接調(diào)用。與load方法不同的是,initialize方法是在第一次調(diào)用類所屬的方法時(shí),才會(huì)調(diào)用initialize方法,而load方法是在main函數(shù)之前就全部調(diào)用了。所以理論上來說initialize可能永遠(yuǎn)都不會(huì)執(zhí)行,如果當(dāng)前類的方法永遠(yuǎn)不被調(diào)用的話。
下面我們研究一下initialize在Runtime中的源碼。
在向?qū)ο蟀l(fā)送消息時(shí),lookUpImpOrForward函數(shù)中會(huì)判斷當(dāng)前類是否被初始化,如果沒有被初始化,則先進(jìn)行初始化再調(diào)用類的方法。
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver);
// ....省略好多代碼
// 第一次調(diào)用當(dāng)前類的話,執(zhí)行initialize的代碼
if (initialize && !cls->isInitialized()) {
_class_initialize (_class_getNonMetaClass(cls, inst));
}
// ....省略好多代碼
在進(jìn)行初始化的時(shí)候,和load方法的調(diào)用順序一樣,會(huì)按照繼承者鏈先初始化父類。_class_initialize函數(shù)中關(guān)鍵的兩行代碼是callInitialize和lockAndFinishInitializing的調(diào)用。
// 第一次調(diào)用類的方法,初始化類對(duì)象
void _class_initialize(Class cls)
{
Class supercls;
bool reallyInitialize = NO;
// 遞歸初始化父類。initizlize不用顯式的調(diào)用super,因?yàn)閞untime已經(jīng)在內(nèi)部調(diào)用了
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
if (reallyInitialize) {
_setThisThreadIsInitializingClass(cls);
if (MultithreadedForkChild) {
performForkChildInitialize(cls, supercls);
return;
}
@try {
// 通過objc_msgSend()函數(shù)調(diào)用initialize方法
callInitialize(cls);
}
@catch (...) {
@throw;
}
@finally {
// 執(zhí)行initialize方法后,進(jìn)行系統(tǒng)的initialize過程
lockAndFinishInitializing(cls, supercls);
}
return;
}
else if (cls->isInitializing()) {
if (_thisThreadIsInitializingClass(cls)) {
return;
} else if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
return;
} else {
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
}
通過objc_msgSend函數(shù)調(diào)用initialize方法。
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
lockAndFinishInitializing函數(shù)中會(huì)完成一些初始化操作,其內(nèi)部會(huì)調(diào)用_finishInitializing函數(shù),在函數(shù)內(nèi)部會(huì)調(diào)用class的setInitialized函數(shù),核心工作都由setInitialized函數(shù)完成。
static void lockAndFinishInitializing(Class cls, Class supercls)
{
monitor_locker_t lock(classInitLock);
if (!supercls || supercls->isInitialized()) {
_finishInitializing(cls, supercls);
} else {
_finishInitializingAfter(cls, supercls);
}
}
負(fù)責(zé)初始化類和元類,函數(shù)內(nèi)部主要是查找當(dāng)前類和元類中是否定義了某些方法,然后根據(jù)查找結(jié)果設(shè)置類和元類的一些標(biāo)志位。
void
objc_class::setInitialized()
{
Class metacls;
Class cls;
// 獲取類和元類對(duì)象
cls = (Class)this;
metacls = cls->ISA();
bool inherited;
bool metaCustomAWZ = NO;
if (MetaclassNSObjectAWZSwizzled) {
metaCustomAWZ = YES;
inherited = NO;
}
else if (metacls == classNSObject()->ISA()) {
// 查找是否實(shí)現(xiàn)了alloc和allocWithZone方法
auto& methods = metacls->data()->methods;
for (auto mlists = methods.beginCategoryMethodLists(),
end = methods.endCategoryMethodLists(metacls);
mlists != end;
++mlists)
{
if (methodListImplementsAWZ(*mlists)) {
metaCustomAWZ = YES;
inherited = NO;
break;
}
}
}
else if (metacls->superclass->hasCustomAWZ()) {
metaCustomAWZ = YES;
inherited = YES;
}
else {
auto& methods = metacls->data()->methods;
for (auto mlists = methods.beginLists(),
end = methods.endLists();
mlists != end;
++mlists)
{
if (methodListImplementsAWZ(*mlists)) {
metaCustomAWZ = YES;
inherited = NO;
break;
}
}
}
if (!metaCustomAWZ) metacls->setHasDefaultAWZ();
if (PrintCustomAWZ && metaCustomAWZ) metacls->printCustomAWZ(inherited);
bool clsCustomRR = NO;
if (ClassNSObjectRRSwizzled) {
clsCustomRR = YES;
inherited = NO;
}
// 查找元類是否實(shí)現(xiàn)MRC方法,如果是則進(jìn)入if語句中
if (cls == classNSObject()) {
auto& methods = cls->data()->methods;
for (auto mlists = methods.beginCategoryMethodLists(),
end = methods.endCategoryMethodLists(cls);
mlists != end;
++mlists)
{
if (methodListImplementsRR(*mlists)) {
clsCustomRR = YES;
inherited = NO;
break;
}
}
}
else if (!cls->superclass) {
clsCustomRR = YES;
inherited = NO;
}
else if (cls->superclass->hasCustomRR()) {
clsCustomRR = YES;
inherited = YES;
}
else {
// 查找類是否實(shí)現(xiàn)MRC方法,如果是則進(jìn)入if語句中
auto& methods = cls->data()->methods;
for (auto mlists = methods.beginLists(),
end = methods.endLists();
mlists != end;
++mlists)
{
if (methodListImplementsRR(*mlists)) {
clsCustomRR = YES;
inherited = NO;
break;
}
}
}
if (!clsCustomRR) cls->setHasDefaultRR();
if (PrintCustomRR && clsCustomRR) cls->printCustomRR(inherited);
metacls->changeInfo(RW_INITIALIZED, RW_INITIALIZING);
}
需要注意的是,initialize方法和load方法不太一樣,Category中定義的initialize方法會(huì)覆蓋原方法而不是像load方法一樣都可以調(diào)用。
簡書由于排版的問題,閱讀體驗(yàn)并不好,布局、圖片顯示、代碼等很多問題。所以建議到我Github上,下載Runtime PDF合集。把所有Runtime文章總計(jì)九篇,都寫在這個(gè)PDF中,而且左側(cè)有目錄,方便閱讀。

下載地址:Runtime PDF
麻煩各位大佬點(diǎn)個(gè)贊,謝謝!??