load_images
- 進入
load_images源碼實現(xiàn),如下
void
load_images(const char *path __unused, const struct mach_header *mh)
{
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
didInitialAttachCategories = true;
loadAllCategories(); // 加載所有分類
}
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods // +load 方法
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
load_images 方法的主要作用是加載鏡像文件,其中關鍵代碼為 prepare_load_methods(加載)、call_load_methods(調用),兩個方法
1. prepare_load_methods
- 進入
prepare_load_methods的源碼,實現(xiàn)如下
void prepare_load_methods(const headerType *mhdr)
{
size_t count, I;
runtimeLock.assertLocked();
classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count); // 獲取 Mach-O 中的靜態(tài)段 __objc_nlclslist 即非懶加載類
for (i = 0; i < count; i++) {
// 將所有的 +load 方法與類綁定加入 loadable_classes 表中
schedule_class_load(remapClass(classlist[i]));
}
// 獲取 Mach-O 中的靜態(tài)段 __objc_nlcatlist,即非懶加載分類,并 load 方法加入表中
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");
}
realizeClassWithoutSwift(cls, nil);
ASSERT(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
- 通過
_getObjc2NonlazyClassList -> schedule_class_load獲取非懶加載類列表,將所有的 +load 方法與類綁定加入 loadable_classes 表中。schedule_class_load的源碼實現(xiàn)如下
- 通過
static void schedule_class_load(Class cls)
{
if (!cls) return;
ASSERT(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
// 確保父類優(yōu)先加載,遞歸,直到父類不存在
schedule_class_load(cls->superclass);
// 將所有的 +load 方法的類跟 load 方法綁定加入 loadable_classes 表中
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
其中,schedule_class_load(cls->superclass); 為了確保它的父類優(yōu)先加載,是一層遞歸循環(huán),直到根類(NSObject)的父類(nil)。add_class_to_loadable_list 是將類的 load 方法和 cls 類名一起加到 loadable_classes 表中,源碼實現(xiàn)如下
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod(); // 獲取類的 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());
}
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[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
獲取類的 load 方法的源碼如下
IMP
objc_class::getLoadMethod()
{
runtimeLock.assertLocked();
const method_list_t *mlist;
ASSERT(isRealized());
ASSERT(ISA()->isRealized());
ASSERT(!isMetaClass());
ASSERT(ISA()->isMetaClass());
mlist = ISA()->data()->ro()->baseMethods();
if (mlist) {
for (const auto& meth : *mlist) {
const char *name = sel_cname(meth.name);
if (0 == strcmp(name, "load")) {
return meth.imp;
}
}
}
return nil;
}
通過獲取 ro 中的方法列表,循環(huán)遍歷,直到找到方法名為 load 的 sel,返回 load 的函數(shù)指針。
- 通過
_getObjc2NonlazyCategoryList獲取非懶加載分類列表,循環(huán)遍歷每個分類。realizeClassWithoutSwift -> add_category_to_loadable_list將分類的load方法加入loadable_categories表中。add_category_to_loadable_list的源碼實現(xiàn)如下
- 通過
void add_category_to_loadable_list(Category cat)
{
IMP method;
loadMethodLock.assertLocked();
method = _category_getLoadMethod(cat); // 獲取分類的 load 方法
// Don't bother if cat has no +load method
if (!method) return;
if (PrintLoading) {
_objc_inform("LOAD: category '%s(%s)' scheduled for +load",
_category_getClassName(cat), _category_getName(cat));
}
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[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
2. call_load_methods
源碼實現(xiàn)如下
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.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
從源碼注釋中可以看到,關鍵代碼是在一個 do - while 循環(huán)中,主要有三個部分
- 反復調用類的
+load方法,直到?jīng)]有
- 反復調用類的
call_class_loads 的源碼實現(xiàn)如下

- 只調用一次分類的
+load方法
- 只調用一次分類的
call_category_loads 的源碼實現(xiàn)如下

- 如果有類或更多未嘗試的分類,則運行更多的+load
在 call_class_loads 和 call_category_loads 中 load 消息發(fā)送 (*load_method)(cls, @selector(load)); 有兩個隱藏參數(shù),第一個為 id 即self,第二個為 sel,即 cmd。

分類(category)與類擴展(extension)
Category 類別、分類
專門用來給分類添加新的方法
不能給類添加成員屬性,添加了成員變量,也無法取到(注意:可以通過 runtime 給分類添加屬性)
分類中用
@property定義變量,只會生成變量的setter,getter方法的聲明,不能生成方法實現(xiàn)和帶下劃線的成員變量
Extension 類擴展
可以說成是特殊的分類,也稱作匿名函數(shù)
可以給類添加成員屬性,但是是私有變量
可以給類添加方法,也是私有方法
類擴展的底層原理探索
創(chuàng)建方式有兩種
- 創(chuàng)建一個
NSObject類,直接在類中(.m 文件)添加代碼(只能在類的聲明之后,實現(xiàn)之前添加)
- 創(chuàng)建一個

- 通過新建(command + N)-> Objective-C File

選擇 Extension 類型、選擇要添加拓展的主類,創(chuàng)建

類擴展的本質
寫一個類擴展,如下

通過 clang 底層編譯探索
- 執(zhí)行
clang -rewrite-objc main.mm -o main.cpp生成 cpp 文件,打開 cpp 文件,首先我們看下屬性lg_name,搜索lg_name,如下

可以看到編譯過程中生成了帶下劃線的成員變量以及 setter、getter 方法。再來看下類擴展中的方法

從上面我們可以得知,在編譯的過程中,類擴展中的方法被添加到 methodlist 中成為了類的一部分,即編譯時期直接添加到本類。
通過源碼探索
- 創(chuàng)建主類
LGPerson類,并實現(xiàn)擴展(LGPerson+Ext)中的方法
/** ------.h ------*/
@interface LGPerson : NSObject
@end
/** ------.m ------*/
#import "LGPerson.h"
#import "LGPerson+Ext.h"
@implementation LGPerson
+ (void)load {
}
- (void)ext_instanceMethod {
}
- (void)ext_classMethod {
}
- (void)instanceMethod {
}
- (void)classMethod {
}
@end
/** ------ LGPerson+Ext.h ------*/
#import "LGPerson.h"
@interface LGPerson ()
@property (nonatomic, copy) NSString *lg_name;
- (void)ext_instanceMethod;
- (void)ext_classMethod;
@end
不導入 LGPerson+Ext.h
運行
objc源碼,在readClass打個斷點,查看此時的ro情況

可以看到此時有四個方法,分別打印出來

可以看到此時打印的是 LGPerson.m 中實現(xiàn)的四個方法
- 導入 LGPerson+Ext.h
按照上面的再來一次,看看此時的 ro 情況

方法列表中有 7 個方法,分別打印出來

此時在拓展類(LGPerson+Ext.h)聲明的屬性也實現(xiàn) setter、getter 方法以及一個 .cxx 方法
類的擴展在編譯期間會作為類的一部分,和類一起編譯進來
類的擴展只是聲明,依賴于當前的主類,沒有.m文件