本文主要通過(guò)OC的源碼剖析了與Category相關(guān)的原理。其中包括了Category的運(yùn)行時(shí)方法屬性管理、load方法原理、initialize方法原理、關(guān)聯(lián)對(duì)象的原理。
先幾個(gè)我們熟知的幾個(gè)知識(shí)點(diǎn)
- 查找一個(gè)方法的時(shí)候先從Category列表里找,依次找到主類(lèi)的方法列表。
- Category列表的方法順序是有編譯順序決定的
- 在程序運(yùn)行的時(shí)候,runtime會(huì)將Category的數(shù)據(jù),合并到類(lèi)信息中(類(lèi)對(duì)象、元類(lèi)對(duì)象中)
- Category的load方法在runtime加載類(lèi)、分類(lèi)的時(shí)候調(diào)用
- 不能直接給Category添加成員變量,但是可以間接實(shí)現(xiàn)Category有成員變量的效果
以上這幾個(gè)知識(shí)點(diǎn)是我們面試中常見(jiàn)到的,今天我們就通過(guò)源碼去了解背后的原因。
在開(kāi)始剖析之前需要大概了解一下objc_class的內(nèi)存結(jié)構(gòu),附上一張圖和一篇之前寫(xiě)的帖子Objective-C的內(nèi)存結(jié)構(gòu)。

Category方法列表
編寫(xiě)一段oc代碼
先定義一個(gè)Person類(lèi)和一個(gè)Person+Test的分類(lèi),然后編譯成C++代碼,在程序啟動(dòng)之前,編譯器會(huì)先將各個(gè)屬性參數(shù)、方法列表等信息準(zhǔn)備好。
// 定義一個(gè)Person類(lèi)
#import "Person.h"
@implementation Person
- (void)abc {}
- (void)run {
NSLog(@"Person - run");
}
+ (void)run2 {}
@end
// 定義一個(gè)Person+test分類(lèi)
#import "Person+Test.h"
@implementation Person (Test)
- (void)run {
NSLog(@"Person (Test) - run");
}
- (void)test {
NSLog(@"test");
}
+ (void)test2 {}
@end
編譯成C++代碼
執(zhí)行命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person.m -o Person.cpp 將person編譯成c++文件。
- Person的方法結(jié)構(gòu)體
//實(shí)例方法abc 和 run 的結(jié)構(gòu)體,包含了2個(gè)元素的方法數(shù)組
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_INSTANCE_METHODS_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"abc", "v16@0:8", (void *)_I_Person_abc},
{(struct objc_selector *)"run", "v16@0:8", (void *)_I_Person_run}}
};
// 類(lèi)方法run2結(jié)構(gòu)體,包含了1個(gè)元素的方法數(shù)組
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CLASS_METHODS_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"run2", "v16@0:8", (void *)_C_Person_run2}}
};
// _class_ro_t 類(lèi)方法結(jié)構(gòu)體,包含了上邊的類(lèi)方法列表數(shù)據(jù)
static struct _class_ro_t _OBJC_METACLASS_RO_$_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1, sizeof(struct _class_t), sizeof(struct _class_t),
0,
"Person",
(const struct _method_list_t *)&_OBJC_$_CLASS_METHODS_Person,
0,
0,
0,
0,
};
// _class_ro_t 實(shí)例方法結(jié)構(gòu)體,包含了上邊實(shí)例方法列表數(shù)據(jù)
static struct _class_ro_t _OBJC_CLASS_RO_$_Person __attribute__ ((used, section ("__DATA,__objc_const"))) = {
0, sizeof(struct Person_IMPL), sizeof(struct Person_IMPL),
0,
"Person",
(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_Person,
0,
0,
0,
0,
};
// 元類(lèi)
extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;
extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_Person __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_NSObject,
0, // &OBJC_METACLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_METACLASS_RO_$_Person,
};
// 類(lèi)
extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;
extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_Person __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_Person,
0, // &OBJC_CLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_CLASS_RO_$_Person,
};
static void OBJC_CLASS_SETUP_$_Person(void ) {
// 元類(lèi)的地址
OBJC_METACLASS_$_Person.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_Person.superclass = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_Person.cache = &_objc_empty_cache;
// 類(lèi)的地址
OBJC_CLASS_$_Person.isa = &OBJC_METACLASS_$_Person;
OBJC_CLASS_$_Person.superclass = &OBJC_CLASS_$_NSObject;
OBJC_CLASS_$_Person.cache = &_objc_empty_cache;
}
- Person+Test.cpp
- 分類(lèi)的結(jié)構(gòu)體
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
// 分類(lèi)實(shí)例方法結(jié)構(gòu)體,包含實(shí)例方法列表
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"run", "v16@0:8", (void *)_I_Person_Test_run},
{(struct objc_selector *)"test", "v16@0:8", (void *)_I_Person_Test_test}}
};
// 分類(lèi)類(lèi)方法結(jié)構(gòu)體,包含類(lèi)方法列表
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"test2", "v16@0:8", (void *)_C_Person_Test_test2}}
};
// category 結(jié)構(gòu)體
static struct _category_t _OBJC_$_CATEGORY_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Person",
0, // &OBJC_CLASS_$_Person,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test,
0,
0,
};
通過(guò)C++文件,我們能大概看出編譯后的類(lèi)和分類(lèi)的代碼結(jié)構(gòu),這是一個(gè)靜態(tài)編譯的結(jié)果,為了之后分析運(yùn)行時(shí)做的儲(chǔ)備。
- 方法是一個(gè)結(jié)構(gòu)體指針,存放著方法列表
- 實(shí)例方法和類(lèi)方法存放在兩個(gè)不同的結(jié)構(gòu)體里
- 存在兩個(gè)結(jié)構(gòu)體變量,一個(gè)是類(lèi)的結(jié)構(gòu)體,一個(gè)是元類(lèi)的結(jié)構(gòu)體
- category的結(jié)構(gòu)體里存放著實(shí)例方法和類(lèi)方法的結(jié)構(gòu)體地址
分析運(yùn)行時(shí)
我們這里使用的是最新版本的oc代碼來(lái)分析,下載代碼時(shí)候,對(duì)應(yīng)的iOS13.3版本,oc代碼版本號(hào)objc4-756.2,可以去apple的opensource網(wǎng)站下載。
這是源碼中category結(jié)構(gòu)體,跟我們上邊的編譯后的C++代碼是一樣的
struct category_t {
const char *name; // 類(lèi)名 Psrson
classref_t cls; // 類(lèi)結(jié)構(gòu)體地址
struct method_list_t *instanceMethods; // 實(shí)例方法
struct method_list_t *classMethods; // 類(lèi)方法
struct protocol_list_t *protocols; // 協(xié)議
struct property_list_t *instanceProperties; // 實(shí)例屬性
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties; //
// 元類(lèi)方法還是實(shí)例方法
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
源碼解讀順序
objc-os.mm 調(diào)用順序
- _objc_init
是OC運(yùn)行時(shí)的入口函數(shù) - map_images
- map_images_nolock
objc-runtime-new.mm 調(diào)用順序
- _read_images 加載鏡像
- remethodizeClass 重新組裝類(lèi)方法
- attachCategories
- attachLists
- realloc、memmove、 memcpy
// 下邊的代碼中,為了方便查看源代碼有刪減,只保留的關(guān)鍵邏輯
// 加載鏡像
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses) {
// 開(kāi)始處理category.
for (EACH_HEADER) {
//獲取到所有的category列表,category放在二維數(shù)組里,每個(gè)數(shù)組存放著一個(gè)類(lèi)的所有category
//這個(gè)數(shù)據(jù)存放在代碼段的__objc_catlist中,感興趣的話可以查看Meth-O文件。
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
//遍歷二維數(shù)組
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
// 判斷是否是實(shí)例方法,這個(gè)對(duì)應(yīng)了我們上邊編譯好的C++代碼
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
// 將對(duì)應(yīng)的category的方法列表放入到NXMap里
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
// 對(duì)類(lèi)方法進(jìn)行重組
remethodizeClass(cls);
classExists = YES;
}
}
// 是否是類(lèi)方法
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
// 給元類(lèi)對(duì)象重組方法
remethodizeClass(cls->ISA());
}
}
}
}
}
//重組方法
static void remethodizeClass(Class cls)
{
attachCategories(cls, cats, true /*flush caches*/);
}
// 給類(lèi)、元類(lèi)附加方法
// 參數(shù):比如上邊的代碼示例
// cls = Person.class
// cats = [category_t(Test)]
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
bool isMeta = cls->isMetaClass();
// fixme rearrange to remove these intermediate allocations
// 申請(qǐng)二維數(shù)組結(jié)構(gòu)體空間,空間大小是一個(gè)類(lèi)的所有分類(lèi)的個(gè)數(shù)
/**方法數(shù)組
[
[method_t, method_t],
[method_t, method_t],
]
*/
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));
/**屬性數(shù)組
[
[property_t, property_t],
[property_t, property_t],
]
*/
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));
/**協(xié)議數(shù)組
[
[protocol_t, protocol_t],
[protocol_t, protocol_t],
]
*/
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int propcount = 0;
int protocount = 0;
int i = cats->count;
bool fromBundle = NO;
// 這里使用了i--,將方法列表倒序放入數(shù)組中,所以最后面編譯的category在數(shù)組的最前邊
while (i--) {
// 取出某個(gè)分類(lèi)
auto& entry = cats->list[i];
// 取出分類(lèi)中的類(lèi)方法或者元類(lèi)方法,插入數(shù)組中
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
// 得到類(lèi)對(duì)象數(shù)據(jù)
auto rw = cls->data();
// 將所有分類(lèi)的對(duì)象方法,附加到類(lèi)對(duì)象的方法列表中
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
// 將所有分類(lèi)的屬性列表,附加到類(lèi)對(duì)象的屬性列表中
rw->properties.attachLists(proplists, propcount);
free(proplists);
// 將所有分類(lèi)的協(xié)議列表,附加到類(lèi)對(duì)象的協(xié)議列表中
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
/** 將一個(gè)數(shù)組的數(shù)據(jù)插入到另一個(gè)數(shù)組的前邊
addedLists
[
[method_t, method_t],
[method_t, method_t],
]
addedCount
*/
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
// 如果原來(lái)的列表中有數(shù)據(jù)
if (hasArray()) {
// many lists -> many lists
// 原來(lái)方法列表的數(shù)量
uint32_t oldCount = array()->count;
// 插入方法列表之后的數(shù)量
uint32_t newCount = oldCount + addedCount;
// 重新分配內(nèi)存
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
// 先將老數(shù)據(jù)向后移動(dòng)addedCount位置
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
// 將新數(shù)據(jù)copy進(jìn)來(lái)
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
// 如果原來(lái)的數(shù)據(jù)為空,并且新數(shù)據(jù)只有一個(gè)元素,那么直接復(fù)制
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;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
總結(jié)一下,以Person為例:
- 程序啟動(dòng)時(shí),將Person的category分類(lèi)列表載入內(nèi)存,順序?yàn)榫幾g順序,[method_t(test01),method_t(test02)]
- 處理數(shù)組順序,將最后編譯的category數(shù)據(jù),放在數(shù)組的最前邊。
- 處理類(lèi)對(duì)象、元類(lèi)對(duì)象數(shù)據(jù),將category數(shù)據(jù)列表,插入到類(lèi)對(duì)象、元類(lèi)對(duì)象數(shù)據(jù)列表的最前邊。
+load方法的調(diào)用時(shí)機(jī)
上邊我們分析源碼的時(shí)候,提到了_objc_init 入口函數(shù)。
這個(gè)函數(shù)里調(diào)用了load_images方法,接下來(lái)調(diào)用了call_load_methods方法,我們看下源碼
void call_load_methods(void)
{
do {
// 1. 加載類(lèi)的load方法
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2.加載分類(lèi)的load方法
more_categories = call_category_loads();
} while (loadable_classes_used > 0 || more_categories);
}
//1.加載類(lèi)的load方法
static void call_class_loads(void)
{
// 遍歷所有class,通過(guò)函數(shù)指針直接調(diào)用load方法
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
(*load_method)(cls, SEL_load);
}
}
// 2.加載分類(lèi)的load方法
static bool call_category_loads(void)
{
// 遍歷所有category
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
// 獲取分類(lèi)的class,通過(guò)函數(shù)指針直接調(diào)用load方法
cls = _category_getClass(cat);
}
return new_categories_added;
}
通過(guò)源碼可以看到
- +load方法會(huì)在runtime加載類(lèi)、分類(lèi)時(shí)調(diào)用
- 每個(gè)類(lèi)、分類(lèi)的+load,在程序運(yùn)行過(guò)程中只調(diào)用一次
- 調(diào)用順序
先調(diào)用類(lèi)的+load,按照編譯先后順序調(diào)用(先編譯,先調(diào)用,調(diào)用子類(lèi)的+load之前會(huì)先調(diào)用父類(lèi)的+load
再調(diào)用分類(lèi)的+load,按照編譯先后順序調(diào)用(先編譯,先調(diào)用)
+ initialize方法的調(diào)用時(shí)機(jī)
initialize調(diào)用過(guò)程
class_getInstanceMethod
lookUpImpOrNil
lookUpImpOrForward
initializeAndLeaveLocked
initializeAndMaybeRelock
initializeNonMetaClass
callInitialize
objc_msgSend(cls, SEL_initialize)
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
// 沒(méi)有初始化過(guò),才進(jìn)行初始化
if (initialize && !cls->isInitialized()) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
}
//初始化class
void initializeNonMetaClass(Class cls)
{
supercls = cls->superclass;
// 遞歸調(diào)用父類(lèi)的初始化
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
if (reallyInitialize) {
// 初始化函數(shù)
callInitialize(cls); // ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
}
}
總結(jié)一下
load、initialize方法的區(qū)別
調(diào)用方式
1、load是根據(jù)函數(shù)地址直接調(diào)用
2、initialize是通過(guò)objc_msgSend調(diào)用調(diào)用時(shí)刻
1、load是runtime加載類(lèi)、分類(lèi)的時(shí)候調(diào)用(只會(huì)調(diào)用1次)
2、initialize是類(lèi)第一次接收到消息的時(shí)候調(diào)用,每一個(gè)類(lèi)只會(huì)initialize一次(父類(lèi)的initialize方法可能會(huì)被調(diào)用多次)
load、initialize的調(diào)用順序
- load
1、 先調(diào)用類(lèi)的load
a) 先編譯的類(lèi),優(yōu)先調(diào)用load
b) 調(diào)用子類(lèi)的load之前,會(huì)先調(diào)用父類(lèi)的load
2、再調(diào)用分類(lèi)的load
a) 先編譯的分類(lèi),優(yōu)先調(diào)用load
- initialize
1、 先初始化父類(lèi)
2、再初始化子類(lèi)(可能最終調(diào)用的是父類(lèi)的initialize方法)
關(guān)聯(lián)對(duì)象
開(kāi)發(fā)過(guò)程中,給category屬性賦值,通常會(huì)用到objc_setAssociatedObject 和 objc_getAssociatedObject方法,接下來(lái)看看關(guān)聯(lián)對(duì)象在源碼中是怎么實(shí)現(xiàn)的。
大概的調(diào)用流程是這樣的

//objc_setAssociatedObject
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
if (!object && !value) return;
assert(object);
if (object->getIsa()->forbidsAssociatedObjects())
_objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
// 單利,管理所有的關(guān)聯(lián)數(shù)據(jù)
AssociationsManager manager;
// 獲取所有的關(guān)聯(lián)對(duì)象的map
AssociationsHashMap &associations(manager.associations());
// 通過(guò)傳進(jìn)來(lái)的對(duì)象生成一個(gè)key
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
// 通過(guò)key獲取一個(gè)value,這個(gè)value是這個(gè)對(duì)象的所有關(guān)聯(lián)屬性的map
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
// 通過(guò)外邊傳進(jìn)來(lái)的key查找到與這個(gè)對(duì)象相關(guān)聯(lián)的屬性map
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
// 更新map中的值,包含了內(nèi)存管理方式和具體的value
j->second = ObjcAssociation(policy, new_value);
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// 第一次賦值
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
// 如果傳進(jìn)來(lái)的value是nil,則擦除key對(duì)應(yīng)的value
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
}
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
ObjcAssociation &entry = j->second;
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;
}
總結(jié)
通過(guò)category我們了解到了類(lèi)的方法列表相關(guān)的內(nèi)存結(jié)構(gòu),load、initialize方法的調(diào)用時(shí)機(jī),關(guān)聯(lián)對(duì)象的調(diào)用原理,再結(jié)合之前的一篇oc的Objective-C的內(nèi)存結(jié)構(gòu)文章,我們大概整體了解了OC對(duì)象的內(nèi)存結(jié)構(gòu)。
其實(shí)關(guān)于OC語(yǔ)法的相關(guān)內(nèi)容,源碼中都能找到答案。只要靜下心來(lái),用心讀源碼,很多問(wèn)題都會(huì)迎刃而解。
如果你覺(jué)得有用,給個(gè)點(diǎn)贊吧。