本文基于objc4-709源碼進(jìn)行分析蝶糯。
1.數(shù)據(jù)結(jié)構(gòu)
在 objc-private.h 文件中只估,可以看到 category 是 category_t 結(jié)構(gòu)體的指針。
typedef struct category_t *Category;
struct category_t {
const char *name;//類的名字
classref_t cls;//要擴(kuò)展的類對象
struct method_list_t *instanceMethods;//實(shí)例方法
struct method_list_t *classMethods;//類方法
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;//類屬性
//根據(jù)當(dāng)前類是否元類返回實(shí)例方法或者類方法
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
//根據(jù)當(dāng)前類是否元類返回實(shí)例屬性或者類屬性
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
可以看到,其中存儲了可以擴(kuò)展的實(shí)例方法捐韩、類方法库继、協(xié)議箩艺、實(shí)例屬性、類屬性制跟。其中類屬性是2016年Xcode8后開始新增的特性舅桩,為了與swift中的 type property 相互操作而引入的,類屬性如何創(chuàng)建雨膨、使用這里不做展開擂涛。
category_list結(jié)構(gòu)體用于存儲所有的category。
typedef locstamped_category_list_t category_list;
struct locstamped_category_list_t {
uint32_t count;//category的數(shù)量
#if __LP64__
uint32_t reserved;
#endif
locstamped_category_t list[0]; //動(dòng)態(tài)申請內(nèi)存的寫法
};
struct locstamped_category_t {
category_t *cat;
struct header_info *hi;
};
locstamped_category_t 存儲 category_t 以及對應(yīng)的 header_info聊记。header_info 存儲了實(shí)體在鏡像中的加載和初始化狀態(tài)撒妈,以及一些偏移量,在加載 Mach-O 文件相關(guān)函數(shù)中經(jīng)常用到排监。
2.category 的加載
找到runtime的加載入口函數(shù):
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);
}
在方法的最后一行狰右,runtime 通過 dyld 動(dòng)態(tài)加載,調(diào)用棧如下:
加載鏡像文件時(shí)map_images
函數(shù)最終會調(diào)用_read_images
函數(shù)舆床, _read_images
函數(shù)間接調(diào)用到attachCategories
函數(shù)棋蚌,完成向類中添加 category 的工作。
節(jié)選 _read_images
函數(shù)中加載 Category 的代碼段(刪掉部分不太重要的代碼和注釋):
// Discover categories. 查找category
for (EACH_HEADER) {
//獲取category列表挨队,但怎么得到的沒看懂
category_t **catlist = _getObjc2CategoryList(hi, &count);
//是否有類屬性
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
//處理這個(gè)category谷暮。 首先,將category注冊到目標(biāo)類盛垦。 然后湿弦,如果類實(shí)現(xiàn)了,重建類的方法列表(等)腾夯。
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
//把category的實(shí)例方法颊埃、協(xié)議蔬充、實(shí)例屬性添加到類上
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
}
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
//把category的類方法、協(xié)議班利、類屬性添加到元類上
addUnattachedCategoryForClass(cat, cls->ISA(), hi);//注意這里是cls->ISA()
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
}
}
}
這里主要做了幾個(gè)事情:
獲取category列表
將category及其類(或元類)建立映射
如果類饥漫、元類已經(jīng)實(shí)現(xiàn),重建它的方法肥败、協(xié)議趾浅、屬性列表
把實(shí)例對象相關(guān)的category實(shí)例方法、協(xié)議馒稍、實(shí)例屬性添加到類上
把類相關(guān)的category類方法皿哨、協(xié)議、類屬性添加到元類上
對協(xié)議的處理:同時(shí)附加到類纽谒、元類中
addUnattachedCategoryForClass
函數(shù)實(shí)際上把類(元類)和category做一個(gè)關(guān)聯(lián)映射证膨,把category及其類、元類注冊到哈希表中鼓黔。把category的方法央勒、協(xié)議、屬性附加到類上交給了 remethodizeClass
函數(shù)去做澳化。
static void remethodizeClass(Class cls)
{
category_list *cats;
bool isMeta;
runtimeLock.assertWriting();
isMeta = cls->isMetaClass();
// Re-methodizing: check for more categories
//unattachedCategoriesForClass獲取類中還未添加的category列表
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
attachCategories(cls, cats, true /*flush caches*/);
free(cats);
}
}
remethodizeClass
先找出類中還沒添加的category列表崔步,接著交給核心函數(shù) attachCategories
來完成向類中添加category的工作。attachCategories
的實(shí)現(xiàn)代碼有一點(diǎn)點(diǎn)長缎谷,這里稍微簡化一下單獨(dú)拿出添加category method 的實(shí)現(xiàn)簡單講一下井濒,添加協(xié)議、屬性的過程其實(shí)差不多列林。
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;
bool isMeta = cls->isMetaClass();
// fixme rearrange to remove these intermediate allocations
//動(dòng)態(tài)分配內(nèi)存
method_list_t **mlists = (method_list_t **)malloc(cats->count * sizeof(*mlists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int i = cats->count;
bool fromBundle = NO;
while (i--) {
auto& entry = cats->list[i];
//methodsForMeta得到category的類方法或者實(shí)例方法瑞你,根據(jù)是否metaclass來判斷
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
}
//獲取類的數(shù)據(jù)字段
auto rw = cls->data();
//通過attachLists把category中的內(nèi)容添加到類
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
}
涉及到的一些數(shù)據(jù)結(jié)構(gòu):method_array_t 、method_list_t 希痴、 list_array_tt 者甲、 entsize_list_tt 以及函數(shù):attachLists
,在
Rumtime源碼中的類和對象的class_rw_t
砌创、class_ro_t
一節(jié)中虏缸,已經(jīng)分析過。
分析上面這段代碼嫩实。while遍歷取出所有category_list *cats
的category寇钉,根據(jù)當(dāng)前類是否是元類,每一個(gè)category獲取得到它的類方法或者實(shí)例方法列表method_list_t *mlist
舶赔,存入method_list_t **mlists
中,也即把category的方法拼接到一個(gè)二維數(shù)組中谦秧。要注意這里是倒序添加的竟纳,新生成的category的方法會先于舊的category的方法插入撵溃。
接著獲取類的數(shù)據(jù)字段class_rw_t
,通過attachLists
函數(shù)把上述method_list_t *mlist
方法列表添加到類的class_rw_t
中的method_array_t methods
(method_array_t也相當(dāng)與是一個(gè)二維數(shù)組)锥累。
新加的方法列表都會添加到method_array_t
前面缘挑。即原來類的方法列表方法順序是A、B桶略、C语淘,category的方法列表方法順序是D滓侍、E广鳍,插入之后的類方法列表的順序是D、E膝蜈、A鹅心、B吕粗、C。category 的方法被放到了新的方法列表的前面旭愧,runtime在查找方法的時(shí)候是沿著著方法列表從前往后查找的颅筋,一找到目標(biāo)名字的方法就不會繼續(xù)往后找了,這也就是為什么category 會“覆蓋”類的同名方法输枯,對原方法的調(diào)用實(shí)際上會調(diào)用 category 中的方法议泵。
由于在category_t中只有 property_list_t 沒有 ivar_list_t (無法添加實(shí)例變量),并且在class_ro_t 中的ivar_list_t又是只讀的桃熄,在category中的屬性是不會生成實(shí)例變量先口。蘋果這么做的目的是為了保護(hù)class在編譯時(shí)期確定的內(nèi)存空間的連續(xù)性,防止runtime增加的變量造成內(nèi)存重疊蜻拨。
3.Associated Object
在category中可以添加屬性但無法添加實(shí)例變量池充。平時(shí)我們在類中使用@property,編譯器會為我們生成帶下劃線的實(shí)例變量缎讼、getter和setter方法收夸,但是在 category 中就不會這樣。
@interface HXObject : NSObject
@property (nonatomic, strong) NSString *name;
@end
@interface HXObject (AssociateOJ)
@property (nonatomic, strong) NSString *assoProperty;
- (void)hello;
@end
@implementation HXObject (AssociateOJ)
- (void)hello{
self.assoProperty = @"asso";
NSLog(@"%@", self.assoProperty);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
HXObject * hxoj = [[HXObject alloc] init];
[hxoj hello];
}
return 0;
}
其實(shí) Xcode 已經(jīng)給了警告:
運(yùn)行這段代碼血崭,控制臺報(bào)找不到 category 屬性的 setter 方法:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[HXObject setAssoProperty:]: unrecognized selector sent to instance 0x100b17710'
category 的屬性存取方法需要手動(dòng)實(shí)現(xiàn)卧惜,又或者用@dynamic實(shí)現(xiàn)。@dynamic在這里我們不討論夹纫。
一般情況下咽瓷,我們會使用關(guān)聯(lián)對象來為已經(jīng)存在的類添加“屬性”。使用關(guān)聯(lián)對象要引入#import <objc/runtime.h>
頭文件舰讹。
@implementation HXObject (AssociateOJ)
- (void)setAssoProperty:(NSString *)assoProperty {
objc_setAssociatedObject(self, @selector(assoProperty), assoProperty, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)assoProperty {
return objc_getAssociatedObject(self, _cmd);
}
- (void)hello {
self.assoProperty = @"123";
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
HXObject * hxoj = [[HXObject alloc] init];
hxoj.assoProperty = @"asso";
NSLog(@"%@",hxoj.assoProperty);
}
return 0;
}
關(guān)于怎么使用關(guān)聯(lián)對象這里也不會詳談茅姜。
通過objc_getAssociatedObject
和objc_setAssociatedObject
,給category實(shí)現(xiàn)了看起來像屬性的存取方法的接口月匣,還能使用點(diǎn)語法钻洒。通過關(guān)聯(lián)對象模擬了實(shí)例變量奋姿。但仍需要記住的一點(diǎn)是,category不能生成實(shí)例變量素标,也不能給類增添實(shí)例變量称诗。
在分類中,因?yàn)轭惖膶?shí)例變量的布局已經(jīng)固定头遭,使用@property已經(jīng)無法向布局中添加新的實(shí)例變量(這樣做可能會覆蓋子類的實(shí)例變量)寓免,所以我們需要使用關(guān)聯(lián)對象以及兩個(gè)方法來模擬構(gòu)成屬性的三個(gè)要素。
4.關(guān)聯(lián)對象在runtime源碼中的實(shí)現(xiàn)
主要函數(shù)有三個(gè):
//根據(jù)key值獲取對應(yīng)的關(guān)聯(lián)對象
id objc_getAssociatedObject(id object, const void *key);
//以鍵值對的形式添加關(guān)聯(lián)對象计维,參數(shù)value傳入nil可以刪除單個(gè)關(guān)聯(lián)對象
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
//移除所有關(guān)聯(lián)對象
void objc_removeAssociatedObjects(id object);
接下來將對這三個(gè)方法進(jìn)行分析袜香,看看關(guān)聯(lián)對象在runtime中是如何實(shí)現(xiàn)的。首先從objc_setAssociatedObject
函數(shù)入手享潜,但在此之前要先介紹四個(gè)涉及到的類困鸥。
- ObjcAssociation : value 和 policy 保存于此
- ObjectAssociationMap : key 保存于此
- AssociationsManager
- AssociationsHashMap : object 保存于此
ObjcAssociation
ObjcAssociation 這個(gè)類保存了關(guān)聯(lián)策略policy以及關(guān)聯(lián)對象value。其余還有構(gòu)造剑按、析構(gòu)函數(shù)疾就、成員變量的訪問函數(shù)等等實(shí)現(xiàn)都比較簡單。
class ObjcAssociation {
uintptr_t _policy;//關(guān)聯(lián)策略
id _value;//關(guān)聯(lián)對象
public:
//構(gòu)造艺蝴、析構(gòu)函數(shù) 以及成員變量的訪問方法等
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
ObjcAssociation() : _policy(0), _value(nil) {}
uintptr_t policy() const { return _policy; }
id value() const { return _value; }
bool hasValue() { return _value != nil; }
};
ObjectAssociationMap
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
ObjectAssociationMap 維護(hù)了從 key (就是那個(gè) void *
參數(shù))到ObjcAssociation的映射猬腰。
AssociationsManager
spinlock_t AssociationsManagerLock;
class AssociationsManager {
static AssociationsHashMap *_map;
public:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
//獲取_map單例
AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
初始化、析構(gòu)時(shí)分別對自旋鎖 spinlock_t 進(jìn)行 lock 和 unlock 猜敢,以此保證對 AssociationsManager 的操作線程安全姑荷。而 associations 函數(shù)實(shí)際上是獲取了_map單例
AssociationsHashMap
class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
AssociationsHashMap 維護(hù)了從 disguised_ptr_t(實(shí)際上是個(gè)unsigned long) 到 ObjectAssociationMap 的映射。在稍后將會在源碼中看到缩擂,disguised_ptr_t 來自于待添加 assiciated object 的對象鼠冕,所以也即這個(gè)類維護(hù)的是從對象到 ObjectAssociationMap 的映射。
總結(jié)以上內(nèi)容來說胯盯,關(guān)聯(lián)對象是存儲在單獨(dú)的哈希表中的懈费。
4.1objc_setAssociatedObject
objc_setAssociatedObject
函數(shù)的實(shí)現(xiàn)中僅調(diào)用了_object_set_associative_reference
函數(shù)。配合注釋以及上述介紹的四個(gè)相關(guān)類博脑,這個(gè)方法的實(shí)現(xiàn)很好理解憎乙。
/**
@param object 要綁定到哪個(gè)對象上(宿主對象)
@param key key
@param value 關(guān)聯(lián)對象
@param policy 關(guān)聯(lián)策略
*/
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
//1.理解為一個(gè)臨時(shí)的ObjcAssociation,在之后保存原有的ObjcAssociation
ObjcAssociation old_association(0, nil);
//2.根據(jù)策略選擇retain 或 copy 這個(gè)屬性
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
//3.得到AssociationsHashMap單例
AssociationsHashMap &associations(manager.associations());
//4.得到一個(gè)代表對象的obj key(obj key要和key區(qū)分開來)
disguised_ptr_t disguised_object = DISGUISE(object);
//如果傳入的關(guān)聯(lián)對象value != nil
if (new_value) {
// 5.在AssociationsHashMap中根據(jù)obj key查找對應(yīng)的ObjectAssociationMap
AssociationsHashMap::iterator i = associations.find(disguised_object);
// 6.找得到
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
//6.1在ObjectAssociationMap中根據(jù)key查找ObjcAssociation
ObjectAssociationMap::iterator j = refs->find(key);
//6.2找得到叉趣,把原ObjcAssociation存到臨時(shí)的old_association泞边,然后更新新的ObjcAssociation
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
//6.3找不到,就在ObjectAssociationMap中新增一個(gè)'key-ObjcAssociation'映射
(*refs)[key] = ObjcAssociation(policy, new_value);
}
}
//7.找不到
else {
//7.1新建一個(gè)ObjectAssociationMap實(shí)例疗杉,把'對象-ObjectAssociationMap'的映射填入AssociationsHashMap阵谚;把'key-ObjcAssociation'的映射填入ObjectAssociationMap
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();//7.2這個(gè)方法會標(biāo)記對象含有關(guān)聯(lián)對象(將isa_t結(jié)構(gòu)體中的標(biāo)記位has_assoc置為true)
}
}
//8.如果傳入的關(guān)聯(lián)對象value == nil
else {
// 在AssociationsHashMap中根據(jù)obj key查找對應(yīng)的ObjectAssociationMap
AssociationsHashMap::iterator i = associations.find(disguised_object);
//如果找得到,移除ObjectAssociationMap中key對應(yīng)的ObjcAssociation
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);
}
}
}
}
//9.如果原關(guān)聯(lián)對象有值,就釋放該關(guān)聯(lián)對象
if (old_association.hasValue()) ReleaseValue()(old_association);
}
- 創(chuàng)建一個(gè)臨時(shí)的
ObjcAssociation
梢什,在之后保存原有的關(guān)聯(lián)對象闻牡。 - 根據(jù)關(guān)聯(lián)策略選擇
retain
或copy
這個(gè)屬性。 - 創(chuàng)建一個(gè)
AssociationsManager
實(shí)例绳矩,獲取AssociationsHashMap
單例。 - 用
DISGUISE(object)
得到一個(gè)代表對象的obj key(obj key要和key區(qū)分開來)玖翅。 - 如果方法的參數(shù) value != nil翼馆。在
AssociationsHashMap
中根據(jù)obj key查找對應(yīng)的ObjectAssociationMap
。如果方法的參數(shù) value = nil金度,跳到第8步应媚。 - 找得到
ObjectAssociationMap
。接著在ObjectAssociationMap
中根據(jù)key查找ObjcAssociation
猜极。找到中姜,把原ObjcAssociation
存到臨時(shí)的old_association
,然后更新新的ObjcAssociation
跟伏;找不到丢胚,就在ObjectAssociationMap
中新增一個(gè)'key-ObjcAssociation'映射。 - 找不到
ObjectAssociationMap
受扳。新建一個(gè)ObjectAssociationMap
實(shí)例携龟,把'對象-ObjectAssociationMap'的映射填入AssociationsHashMap
;把'key-ObjcAssociation'的映射填入ObjectAssociationMap
勘高。 - 移除
ObjectAssociationMap
中key對應(yīng)的ObjcAssociation
峡蟋。 - 如果原關(guān)聯(lián)對象(
old_association
)有值,就釋放該關(guān)聯(lián)對象
ps:這里注意一下obj key指的是disguised_ptr_t disguised_object = DISGUISE(object)
得到的disguised_object
华望。而key指的是方法的參數(shù)key蕊蝗。
4.2objc_getAssociatedObject
objc_getAssociatedObject
函數(shù)的實(shí)現(xiàn)中僅調(diào)用了_object_get_associative_reference
函數(shù)。代碼中的查找邏輯和objc_setAssociatedObject
中的差不多赖舟,有差別的地方我用注釋寫了一下蓬戚。
id _object_get_associative_reference(id object, void *key) {
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;//默認(rèn)值
{
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和policy
value = entry.value();
policy = entry.policy();
//根據(jù)policy調(diào)用retain方法
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
}
}
}
//根據(jù)policy調(diào)用autorelease方法
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
}
return value;
}
在查找到ObjcAssociation
后獲取其中的value和policy成員,policy的默認(rèn)值是OBJC_ASSOCIATION_ASSIGN
建蹄。根據(jù)獲取得到的policy值決定對value進(jìn)行retain
或者autorelease
.
objc_removeAssociatedObjects
objc_removeAssociatedObjects會先使用hasAssociatedObjects函數(shù)來確認(rèn)對象有沒有關(guān)聯(lián)對象碌更,然后才調(diào)用_object_remove_assocations
進(jìn)行具體的移除操作。
void objc_removeAssociatedObjects(id object)
{
//hasAssociatedObjects確認(rèn)對象有沒有關(guān)聯(lián)對象
if (object && object->hasAssociatedObjects()) {
_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 *refs = i->second;
//把ObjectAssociationMap中的所有ObjcAssociation存到一個(gè)vector中
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
//釋放ObjectAssociationMap洞慎,移除AssociationsHashMap的'對象-ObjectAssociationMap'映射
delete refs;
associations.erase(i);
}
}
//對所有ObjcAssociation調(diào)用ReleaseValue()進(jìn)行釋放
for_each(elements.begin(), elements.end(), ReleaseValue());
}
唔...查找ObjcAssociation
的邏輯一樣的痛单。這里把ObjectAssociationMap
中的所有ObjcAssociation
存到一個(gè)vector中,然后釋放ObjectAssociationMap
劲腿、移除AssociationsHashMap
的'對象-ObjectAssociationMap'映射旭绒,最后對保存在vector中的所有ObjcAssociation
調(diào)用ReleaseValue()
進(jìn)行釋放。
生命周期
對象的銷毀函數(shù):
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);//調(diào)用對象的析構(gòu)函數(shù)
if (assoc) _object_remove_assocations(obj);//移除所有關(guān)聯(lián)對象
obj->clearDeallocating();//清空引用計(jì)數(shù)和weak表
}
return obj;
}
在這個(gè)函數(shù)中我們看到,我們無需關(guān)心關(guān)聯(lián)對象的生命周期挥吵,在銷毀對象時(shí)重父,會檢查這個(gè)對象有沒有關(guān)聯(lián)對象,有的話就調(diào)用_object_remove_assocations
函數(shù)把所有關(guān)聯(lián)對象移除掉忽匈。
ps:根據(jù)Objective-C Associated Objects 的實(shí)現(xiàn)原理一文中的分析房午,
關(guān)聯(lián)對象的釋放時(shí)機(jī)與移除時(shí)機(jī)并不總是一致,比如用關(guān)聯(lián)策略 OBJC_ASSOCIATION_ASSIGN 進(jìn)行關(guān)聯(lián)的對象丹允,很早就已經(jīng)被釋放了(由于autoreleasepool drain而釋放)郭厌,但是并沒有被移除,而再使用這個(gè)關(guān)聯(lián)對象時(shí)就會造成 Crash 雕蔽。
參考文章: