由于分類是不能直接添加成員變量的,因此需要使用關(guān)聯(lián)對(duì)象技術(shù)為分類添加成員變量爱只,由此引出幾個(gè)問題:
- 問題1?:什么是成員變量,成員變量和實(shí)例變量還有屬性的區(qū)別是什么?
- 問題2?:分類中是如何添加成員變量的,其實(shí)現(xiàn)原理是什么寞肖?
- 問題3?:分類中添加的成員變量被保存在哪?(在宿主類上還是其他什么地方)
源碼分析:
objc_setAssociatedObject
入口函數(shù)
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);//其中key做了一次強(qiáng)制類型轉(zhuǎn)換
}
- ?? 參數(shù)
key
是一個(gè)任意類型的常量指針材鹦,可以傳入@selector() 結(jié)構(gòu)體指針SEL類型
主函數(shù)
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
//1.創(chuàng)建一個(gè)空的ObjcAssociation對(duì)象
ObjcAssociation old_association(0, nil);
// 2.對(duì)關(guān)聯(lián)對(duì)象進(jìn)行內(nèi)存管理操作retain/copy
id new_value = value ? acquireValue(value, policy) : nil;
{
//3.創(chuàng)建AssociationsManager對(duì)象和其成員變量AssociationsHashMap對(duì)象
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
//4.將被關(guān)聯(lián)對(duì)象的指針地址反轉(zhuǎn)
disguised_ptr_t disguised_object = DISGUISE(object);
// 5.判斷傳入的關(guān)聯(lián)對(duì)象是否為空
if (new_value) {
//如果不為空
/*通過被關(guān)聯(lián)對(duì)象的指針地址從AssociationsHashMap中獲取對(duì)應(yīng)的
ObjectAssociationMap
*/
AssociationsHashMap::iterator i = associations.find(disguised_object);
//如果該被關(guān)聯(lián)對(duì)象已經(jīng)存在了對(duì)應(yīng)的ObjectAssociationMap
if (i != associations.end()) {
// 通過參數(shù)key獲取到ObjectAssociationMap對(duì)應(yīng)的ObjcAssociation
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
//如果存在對(duì)應(yīng)的ObjcAssociation
if (j != refs->end()) {
//把開始創(chuàng)建的空的ObjcAssociation對(duì)象設(shè)置為找到的這個(gè)ObjcAssociation
old_association = j->second;
//把當(dāng)前的關(guān)聯(lián)對(duì)象封裝為ObjcAssociation并賦值給ObjectAssociationMap中key對(duì)應(yīng)的值
j->second = ObjcAssociation(policy, new_value);
} else {//如果沒有找到存在對(duì)應(yīng)的ObjcAssociation逝淹,那就根據(jù)參數(shù)key直接設(shè)置
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else { //如果該被關(guān)聯(lián)對(duì)象不存在了對(duì)應(yīng)的ObjectAssociationMap
// 那么就創(chuàng)建新的ObjectAssociationMap
ObjectAssociationMap *refs = new ObjectAssociationMap;
//以被關(guān)聯(lián)對(duì)象的反轉(zhuǎn)地址為key,新創(chuàng)建的ObjectAssociationMap為value桶唐,映射為ObjectAssociationHashMap
associations[disguised_object] = refs;
//把參數(shù)key作為ObjectAssociationMap的key栅葡,ObjcAssociation對(duì)象為value
(*refs)[key] = ObjcAssociation(policy, new_value);
//最后設(shè)置這個(gè)被關(guān)聯(lián)對(duì)象為有關(guān)聯(lián)對(duì)象
object->setHasAssociatedObjects();
}
} else {//如果傳入的關(guān)聯(lián)對(duì)象為nil,會(huì)將被關(guān)聯(lián)對(duì)象的對(duì)應(yīng)關(guān)聯(lián)對(duì)象擦除
// setting the association to nil breaks the association.
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);
}
}
}
}
// 最后將old_association擦除
if (old_association.hasValue()) ReleaseValue()(old_association);
}
- ?? 這個(gè)函數(shù)就是主要的實(shí)現(xiàn)函數(shù)尤泽,關(guān)聯(lián)對(duì)象是通過一個(gè)c++類
AssociationsManager
中的一個(gè)靜態(tài)成員變量AssociationsHashMap (也是一個(gè)c++類)
存儲(chǔ)的 - ?? 其中
AssociationsHashMap
是一個(gè)靜態(tài)的欣簇,也就是全局的唯一的副本,是通過哈希查找實(shí)現(xiàn)的坯约,即所有類的關(guān)聯(lián)對(duì)象都放在AssociationsHashMap
中 - ?? 首先會(huì)將參數(shù)value和policy進(jìn)行關(guān)聯(lián)熊咽,并封裝成一個(gè)c++類
ObjcAssociation
,其中ObjcAssociation
有倆個(gè)成員變量闹丐,value
關(guān)聯(lián)對(duì)象的地址和policy
關(guān)聯(lián)策略 - ?? 通過參數(shù)key和封裝好的
ObjcAssociation
映射成一個(gè)ObjectAssociationMap
c++類横殴,查找時(shí)并不是像字典一樣簡(jiǎn)單的通過key去查找value,而是通過紅黑樹查找unordered_map的方式去查找的 - ??最后,被關(guān)聯(lián)對(duì)象指針地址反轉(zhuǎn)作為key衫仑,
ObjectAssociationMap
作為value梨与,映射成一個(gè)AssociationsHashMap
c++類,查找時(shí)并不是像字典一樣簡(jiǎn)單的通過key去查找value文狱,而是通過哈希查找map的方式去查找的 - ??具體的代碼實(shí)現(xiàn)思路見注釋
根據(jù)傳入的關(guān)聯(lián)對(duì)象value和關(guān)聯(lián)策略policy粥鞋,調(diào)用對(duì)應(yīng)的retain/copy方法
static id acquireValue(id value, uintptr_t policy) {
switch (policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
return objc_retain(value);
case OBJC_ASSOCIATION_SETTER_COPY:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
}
return value;
}