Category的用途:
1.類的拆解:根據(jù)不同功能將代碼放入不同分類中(模擬多繼承),減少單個類的體積;
2.公開“私有方法”(私有方法前向引用):類擴展中已聲明私有方法次洼,去分類.h聲明;
- (a) 類擴展中聲明私有方法
@interface Person ()
// 類擴展聲明的私有方法
- (void)ExtensionPrivateMethod ;
@end
@implementation Person
- (void)ExtensionPrivateMethod {
NSLog(@"類擴展@interface()有聲明,私有方法");
}
@end
- (b) 分類.h再次聲明
#import "Person.h"
@interface Person (Category1)
// .h中聲明私有方法遇骑,.m中無需實現(xiàn)
- (void)ExtensionPrivateMethod;
@end
#import "Person+Category1.h"
@implementation Person (Category1)
@end
- (c) 調(diào)用私有方法卖毁,需要引入類和分類
#import "Person.h"
#import "Person+Category1.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
// 私有方法前向引用
[person ExtensionPrivateMethod];
}
return 0;
}
總結(jié):
1.如果不在分類.h中公開私有方法,可以通過performSelector方式強制調(diào)用私有方法或者拿到它的函數(shù)指針調(diào)用
2.正常的方式調(diào)用就需要在分類.h中公開(聲明)私有方法落萎,不實現(xiàn)
Person *person = [[Person alloc] init];
[person performSelector:@selector(ExtensionPrivateMethod)];
Category結(jié)構(gòu)
分類不能直接添加成員變量亥啦,因為category中沒有存儲成員變量對應(yīng)的指針變量;
可以利用關(guān)聯(lián)對象技術(shù)實現(xiàn)為category添加成員變量的效果练链;
分類能添加屬性翔脱,但是不會生成setter和getter方法;
struct category_t {
const char *name; // 分類的名稱
classref_t cls; // 分類所屬的列
struct method_list_t *instanceMethods; // 添加的實例方法列表
struct method_list_t *classMethods; // 添加的類方法列表
struct protocol_list_t *protocols; // 添加的協(xié)議列表
struct property_list_t *instanceProperties; // 添加的實例屬性列表
struct property_list_t *_classProperties; // 類屬性(class, nonatomic, strong)
}
- 關(guān)聯(lián)對象技術(shù)實現(xiàn)為分類添加成員變量
#import "Person+Category.h"
#import <objc/runtime.h> // 類相關(guān)函數(shù)
//#import <objc/message.h> // 消息相關(guān)函數(shù)
@implementation Person (Category)
// 關(guān)聯(lián)對象 能夠為分類添加屬性
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN);
}
- (NSString *)name {
return objc_getAssociatedObject(self, @"name");
}
@end
關(guān)聯(lián)對象API
設(shè)置類關(guān)聯(lián)屬性 void _object_set_associative_reference(object, key, value, policy)
獲取類關(guān)聯(lián)屬性 id _object_get_associative_reference(object, key);
移除類所有關(guān)聯(lián)屬性 void _object_remove_assocations(object);
- 設(shè)置類關(guān)聯(lián)屬性 _object_set_associative_reference(object, key, value, policy)
/**
@param object 準備被關(guān)聯(lián)的對象
@param key 要關(guān)聯(lián)的值對應(yīng)的key 標識
@param value 關(guān)聯(lián)的值
@param policy 策略
*/
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// 存放 policy 和 關(guān)聯(lián)值value 默認為空
ObjcAssociation old_association(0, nil);
// 根據(jù) 策略policy 對 value 進行加工媒鼓,按照策略對value進行 copy或者retain
// newValue ---> 準備關(guān)聯(lián)的值
id new_value = value ? acquireValue(value, policy) : nil;
{
// 關(guān)聯(lián)對象管理類届吁,C++實現(xiàn)一個類
// 維護了一個單例Hash表 AssociationsHashMap對象
AssociationsManager manager;
/**
初始化一個 AssociationsHashMap 對象 associations
用來維護對象 和 ObjectAssociationMap 之間的關(guān)系
單例對象 AssociationsHashMap
*/
AssociationsHashMap &associations(manager.associations());
// 獲取關(guān)聯(lián)對象的索引 ---> DISGUISE 對這個指針地址 按位取反
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// 根據(jù)對象指針查找對應(yīng)的一個ObjectAssociateMap結(jié)構(gòu)的map
AssociationsHashMap::iterator i = associations.find(disguised_object);
// 從全局容器中找到了ObjectAssociationMap
if (i != associations.end()) {
// secondary table exists --> i->second
// i->first 表示對象指針
// i->second 表示獲取 ObjectAssociationMap
ObjectAssociationMap *refs = i->second;
// 根據(jù)傳遞進來的key進行查找
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) { // ObjcAssociation 找到了
old_association = j->second;
// 如果關(guān)聯(lián)對象已存在,則通過ObjcAssociation賦新值(替換最新的value)
j->second = ObjcAssociation(policy, new_value);
} else {
// 如果關(guān)聯(lián)對象不存在隶糕,則創(chuàng)建新的關(guān)聯(lián)對象
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
/**
create the new association (first time).
如果沒有 ObjectAssociationMap 表
則第一次創(chuàng)建一個 ObjectAssociationMap 表
*/
ObjectAssociationMap *refs = new ObjectAssociationMap;
// 全局容器中的key = disguised_object
// 全局容器中的value = 這個新創(chuàng)建的map
associations[disguised_object] = refs;
/**
新關(guān)聯(lián)的值 new_value 通過策略 policy 組裝成 ObjcAssociation
作為新創(chuàng)建好的map, ObjectAssociationMap[key]
key 傳進來的key
*/
(*refs)[key] = ObjcAssociation(policy, new_value);
// 最后設(shè)置這個對象是有關(guān)聯(lián)對象
object->setHasAssociatedObjects();
}
} else {
/**
如果new_value為 空瓷产,那么刪除該關(guān)聯(lián)對象
*/
AssociationsHashMap::iterator i = associations.find(disguised_object);
// 查找到了這個ObjectAssociationMap
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
// 通過key 到 ObjectAssociationMap中查找
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
// 找到了ObjcAssociation
old_association = j->second;
// 擦除操作站玄,從ObjectAssociationMap中移除
// 所以我們想移除關(guān)聯(lián)對象枚驻,可以將關(guān)聯(lián)對象的值設(shè)置為nil進行移除
refs->erase(j);
}
}
}
}
// 釋放舊值
if (old_association.hasValue()) ReleaseValue()(old_association);
}
- 獲取類關(guān)聯(lián)屬性 _object_get_associative_reference(object, key)
// 用來得到已有的關(guān)聯(lián)對象
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
// 初始化AssociationsManager
AssociationsManager manager;
/**
初始化一個AssociationsHashMap 對象 associations
用來維護對象和ObjectAssociationMap之間的關(guān)系
*/
AssociationsHashMap &associations(manager.associations());
// 獲取關(guān)聯(lián)對象的索引
disguised_ptr_t disguised_object = DISGUISE(object);
// 根據(jù)對象指針查找對應(yīng)的一個ObjectAssociateMap結(jié)構(gòu)的map
// 通過迭代器 找到對應(yīng)的 ObjectAssociationMap
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// i->first 表示對象地址
// i->second表示獲取ObjectAssociationMap
ObjectAssociationMap *refs = i->second;
// 通過find(key)找到ObjectAssociationMap
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
// 獲取到ObjcAssociation
ObjcAssociation &entry = j->second;
// 取值 取出value和policy
value = entry.value();
policy = entry.policy();
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
objc_retain(value);
}
}
}
}
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
objc_autorelease(value);
}
return value;
}
- 移除類所有關(guān)聯(lián)對象 _object_remove_assocations(object)
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
if (associations.size() == 0) return;
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// 找到ObjectAssociationMap 開始遍歷
// 拷貝所有需要刪除的關(guān)聯(lián)對象
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// 刪除 ObjectAssociationMap
delete refs;
associations.erase(i);
}
}
// 將拷貝的值再次遍歷release.
for_each(elements.begin(), elements.end(), ReleaseValue());
}
Category的加載過程
- _objc_init() runtime入口函數(shù),進行一些初始化操作
void _objc_init(void) {
static bool initialized = false;
if (initialized) return;
initialized = true;
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
// 注冊了三個回調(diào)函數(shù)
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
- map_images() 加鎖
void map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[]) {
// 加鎖
rwlock_writer_t lock(runtimeLock);
// 完成所有類的注冊 和 fixup 等操作,還包括一些初始化工作以及 調(diào)用load類方法
return map_images_nolock(count, paths, mhdrs);
}
- map_images_nolock() 完成所有類的注冊和fixup等工作,還包括一些初始化工作以及調(diào)用load類方法
void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[]) {
......
......
/**
_read_images 完成兩件事
1掐场、將category和類綁定在一起
2晰筛、重建類的方法列表
*/
if (hCount > 0) {
// 完成類的加載,協(xié)議的加載哟沫,類別的加載等工作
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
}
- _read_images() 完成類、協(xié)議、分類的加載等工作
_read_images()中主要完成了兩件事情
1沽损、addUnattachedCategoryForClass(...) 將category和類綁定在一起
2、remethodizeClass(...)重建類的方法列表
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses) {
......
// 發(fā)現(xiàn)分類
for (EACH_HEADER) {
// 從編譯好的文件獲取所有的分類
category_t **catlist = _getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
// 遍歷category列表
for (i = 0; i < count; i++) {
category_t *cat = catlist[I];
// 重映射分類所屬的類
Class cls = remapClass(cat->cls);
if (!cls) {
catlist[i] = nil;
continue;
}
bool classExists = NO;
if (cat->instanceMethods ||
cat->protocols ||
cat->instanceProperties) {
// 綁定分類和目標類 or 將category添加到分類表中
addUnattachedCategoryForClass(cat, cls, hi);
// 判斷類是否初始化
if (cls->isRealized()) {
// 重新構(gòu)建方法列表
remethodizeClass(cls);
classExists = YES;
}
}
// 把category的類方法和協(xié)議添加到類的metaclass上(元類)
if (cat->classMethods ||
cat->protocols ||
(hasClassProperties && cat->_classProperties)) {
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
}
}
}
if (DebugNonFragileIvars) {
realizeAllClasses();
}
}
// 將category添加到分類表中
static void addUnattachedCategoryForClass(category_t *cat, Class cls,
header_info *catHeader) {
runtimeLock.assertWriting();
// 獲取未處理的分類的表(沒有把分類中的信息添加到類中去)
// NXMapTable 是哈希表
NXMapTable *cats = unattachedCategories();
category_list *list;
// 根據(jù)類獲取對應(yīng)的分類數(shù)組
list = (category_list *)NXMapGet(cats, cls);
if (!list) {
list = (category_list *)
calloc(sizeof(*list) + sizeof(list->list[0]), 1);
} else {
// 擴容
list = (category_list *)
realloc(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
}
// 把當前分類添加到分類數(shù)組中 list中是一系列l(wèi)ocstamp_category_t
list->list[list->count++] = (locstamped_category_t){cat, catHeader};
// 把新的list插入到分類表中去 key=cls, value=list
NXMapInsert(cats, cls, list);
}
- remethodizeClass() 重建類的方法列表
// 重建類的方法列表
static void remethodizeClass(Class cls) {
category_list *cats;
bool isMeta;
runtimeLock.assertWriting();
// 是否是元類
isMeta = cls->isMetaClass();
// 獲取類對應(yīng)的分類數(shù)組循头,并且從分類的哈希表中刪除掉分類數(shù)組
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
// 將分類的method绵估、protocol炎疆、property添加到class中
attachCategories(cls, cats, true /*flush caches*/);
// 釋放分類
free(cats);
}
}
// 獲取cls中未完成整合的所有分類
static category_list *unattachedCategoriesForClass(Class cls, bool realizing) {
runtimeLock.assertWriting();
// 返回對應(yīng)的分類
return (category_list *)NXMapRemove(unattachedCategories(), cls);
}
- attachCategories() 將分類中的方法和屬性列表綁定到目標類上
attachCategories 主要是創(chuàng)建一個新的方法列表空間,存放category中的實例方法国裳、類方法形入、協(xié)議方法,然后將這個方法列表交給attachLists處理
static void attachCategories(Class cls, category_list *cats, bool flush_caches) {
// 對分類進行空判斷缝左,如果沒有直接return
if (!cats) return;
// 判斷是否是元類
bool isMeta = cls->isMetaClass();
/**
方法列表 二維數(shù)組
[
[method_t, method_t],
[method_t],
[method_t, method_t,method_t],
...
]
*/
// 方法列表
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
// 屬性列表
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
// 協(xié)議列表
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
int mcount = 0;
int propcount = 0;
int protocount = 0;
// 原有類的分類的總數(shù)
int i = cats->count;
bool fromBundle = NO;
// 遍歷所有分類亿遂,依次將每個分類里面的方法添加到臨時的數(shù)組中
// 后編譯的分類會先調(diào)用
while (i--) { // 倒序遍歷,最先訪問最后編譯的分類
// 獲取該分類的方法列表
auto& entry = cats->list[I];
// 返回類的方法列表渺杉,并拼接在臨時的方法數(shù)組中
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
// 屬性列表添加規(guī)則 同方法列表添加規(guī)則
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
// 協(xié)議列表添加規(guī)則 同方法列表添加規(guī)則
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
// 獲取宿主類當中的 rw 數(shù)據(jù)蛇数,其中包含宿主類的方法列表信息
auto rw = cls->data();
// 主要是針對 分類中關(guān)于內(nèi)存管理相關(guān)方法情況下的 一些特殊處理
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
/**
rw 代表類
methods 代表類的方法列表
attachLists 方法的含義是 將含有mcount個元素的mlists拼接到 rw 的methods上
*/
// 1、創(chuàng)建新的 方法列表 和 目標類中方法列表融合
// 1是越、把分類中的方法添加class中的方法列表中去
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
// 2苞慢、創(chuàng)建新的 屬性列表 和 目標類的屬性列表融合
// 2、把分類中的屬性添加class中的屬性列表中去
rw->properties.attachLists(proplists, propcount);
free(proplists);
// 3英妓、創(chuàng)建新的 協(xié)議列表 和 目標類的協(xié)議列表融合
// 3挽放、將分類中的協(xié)議列表添加到class中的協(xié)議列表中去
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
- attachLists() 將類中的方法和分類中的方法放到一個列表中
oldCount:目標類中方法列表長度
addedCount:category方法列表的長度
mommove:將類方法后移addedCount個偏移量
momcpy:將category中方法復(fù)制到偏移量從0到addedCount的類方法列表中
所以category中的方法并沒有覆蓋類中的方法,只是將category中的方法放到了類方法的前面蔓纠,調(diào)用方法的時候辑畦,如果category和類中有同名方法,系統(tǒng)會找到前面category中的方法返回并調(diào)用腿倚。category中的方法優(yōu)先級高于類中的方法
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
// 有數(shù)組的情況下
if (hasArray()) {
// many lists -> many lists
// 列表中原有元素總數(shù) oldCount
uint32_t oldCount = array()->count;
// 拼接之后的元素總數(shù)
uint32_t newCount = oldCount + addedCount;
// 根據(jù)新總數(shù)重新分配內(nèi)存
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
// 重新設(shè)置元素總數(shù)
array()->count = newCount;
/**
內(nèi)存移動
[[], [], [], [原有的第一個元素], [原有的第二個元素]]
*/
memmove(array()->lists + addedCount,
array()->lists,
oldCount * sizeof(array()->lists[0]));
/**
內(nèi)存拷貝
[
A ---> [addedLists中的第一個元素]
B ---> [addedLists中第二個元素]
C ---> [addedLists中第三個元素]
[原有的第一個元素]
[原有的第二個元素]
]
*/
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
} else if (!list && addedCount == 1) {
// 0 lists -> 1 list
// 直接將新方法列表首地址賦值給列表
list = addedLists[0];
} else {
// 1 list -> many lists
// 類方法列表只有一個
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
// 將類方法直接移動到列表的addedCount位置
if (oldList) array()->lists[addedCount] = oldList;
// 將Category中的方法拷貝到array數(shù)組中位置從0~addedCount-1前面
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
總結(jié):通過Runtime加載某個類的所有category數(shù)據(jù)纯出,將所有category的方法、屬性敷燎、協(xié)議信息合并到一個大數(shù)組中暂筝,后參與編譯的category數(shù)據(jù),會在數(shù)組的前面(后編譯先調(diào)用while(--))硬贯,將合并后的category數(shù)據(jù)(方法焕襟、屬性、協(xié)議)插入到類原來數(shù)據(jù)的前面
Category的"覆蓋"問題
后編譯的category中的同名方法會覆蓋前面編譯的category中的同名方法
category覆蓋類中的同名方法
可以通過函數(shù)指針調(diào)用原有類中的方法
#import "Person.h"
#import "Person+Category1.h"
#import "Person+Category2.h"
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
[person test];
// 調(diào)用person中的test方法
unsigned int count = 0;
Method *methodList = class_copyMethodList([Person class], &count);
IMP imp = NULL;
SEL sel = NULL;
for (unsigned int i = 0; i < count; i ++) {
Method method = methodList[i];
SEL methodSEL = method_getName(method);
NSString *methodName = [NSString stringWithUTF8String:sel_getName(methodSEL)];
if ([methodName isEqualToString:@"test"]) {
imp = method_getImplementation(method);
sel = method_getName(method);
}
}
((void(*)(id, SEL))(void *)imp)(person, sel);
free(methodList);
}
return 0;
}
load方法加載順序
load_images中主要做了2件事情:
1饭豹、發(fā)現(xiàn)load方法 prepare_load_methods();
2鸵赖、調(diào)用load方法 call_load_methods();
void load_images(const char *path __unused, const struct mach_header *mh) {
// 判斷是否有l(wèi)oad方法,沒有l(wèi)oad方法直接返回
if (!hasLoadMethods((const headerType *)mh)) return;
// 遞歸鎖
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods 發(fā)現(xiàn)load方法
{
// 讀寫鎖
rwlock_writer_t lock2(runtimeLock);
// 發(fā)現(xiàn)load方法
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
// 調(diào)用load方法
call_load_methods();
}
- prepare_load_methods()
prepare_load_methods()中schedule_class_load(isa->superclass) 通過遞歸先將父類中的load方法存入loadable_class拄衰,所有得出父類的load優(yōu)先于子類的load方法調(diào)用
void prepare_load_methods(const headerType *mhdr) {
size_t count, i;
runtimeLock.assertWriting();
// 獲取非懶加載的類的列表
classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
// 核心1:【獲得子類和父類中所有的load方法】load 方法如何別處理在這里 先加載父類 再加載子類
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; // category for ignored weak-linked class 忽略弱連接的分類
// 初始化類信息
realizeClass(cls);
assert(cls->ISA()->isRealized());
// 核心2:添加到loadable_categories
add_category_to_loadable_list(cat);
}
}
static void schedule_class_load(Class cls) {
// 判斷是否為空
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
/**
// load方法已經(jīng)被執(zhí)行過
#define RW_LOADED (1<<23)
和下面的 cls->setInfo(RW_LOADED) 對應(yīng)
*/
// 判斷是否已經(jīng)添加過load方法到load方法列表中
if (cls->data()->flags & RW_LOADED) return;
// 通過遞歸找到父類的load方法并添加它褪,實際上調(diào)用自身的方法
schedule_class_load(cls->superclass);
// 將Class和IMP添加到調(diào)用列表中
add_class_to_loadable_list(cls);
// 設(shè)置class狀態(tài)為已經(jīng)添加過load方法 意思是設(shè)置了一個標志
cls->setInfo(RW_LOADED);
}
- call_load_methods() load方法的調(diào)用
通過do-while循環(huán)中 call_class_loads()和 call_category_load()可以看出,優(yōu)先調(diào)用類的load方法翘悉,再調(diào)用分類的調(diào)用方法
load方法通過函數(shù)指針調(diào)用
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) {
// 1.先調(diào)用類中的+load方法
call_class_loads();
}
// 2. Call category +loads ONCE
// 2.調(diào)用分類的+load方法
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;
}
// 調(diào)用類的load方法
static void call_class_loads(void) {
int i;
// Detach current loadable list.
// 獲取當前l(fā)oadable_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.
// 從子類到父類茫打,遍歷所有類中的 +load 方法
// 遍歷loadable_classes列表,依次執(zhí)行l(wèi)oad方法,只遍歷已添加的load方法的列表元素used
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
// 如果cls為空老赤,則不執(zhí)行其load方法
if (!cls) continue;
// 通過函數(shù)地址直接調(diào)用load方法
(*load_method)(cls, SEL_load);
}
// Destroy the detached list.
if (classes) free(classes);
}