1. 三個方法
objc_setAssociatedObject
objc_getAssociatedObject
objc_removeAssociatedObjects
2原理
2.1objc_setAssociatedObject
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
//同一個對象只會創(chuàng)建一個ObjectAssociationMap對象。蜈彼?
if (i != associations.end()) {
// secondary table exists
//當(dāng)前對象已經(jīng)創(chuàng)建關(guān)聯(lián)對象時
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);//對象置nil后再關(guān)聯(lián)對象
}
} else {
// create the new association (first time).
//當(dāng)前對象沒有創(chuàng)建關(guān)聯(lián)對象時
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.
// 當(dāng)前對象value設(shè)置為nil時杂数,釋放ObjectAssociationMap對象
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);
}
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;
}
- 全局維護AssociationsManager,管理類中存放的是AssociationsHashMap晰韵。
- acquireValue如果是OBJC_ASSOCIATION_SETTER_RETAIN或OBJC_ASSOCIATION_SETTER_COPY靡砌,對value進行retain和copy操作。與引用計數(shù)管理相同。
- 存儲時跟匆,通過DISGUISE(object)作為key查找,associations沒有存儲時會走else
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
inline void
objc_object::setHasAssociatedObjects()
{
...
newisa.has_assoc = true;//關(guān)聯(lián)對象標(biāo)志位
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
}
ObjcAssociation是構(gòu)造存儲對象通砍。
即一個對象會生成一個ObjectAssociationMap玛臂,并通過disguised_object存儲在associations中,new_value存儲在ref中封孙,setHasAssociatedObjects設(shè)置對象有關(guān)聯(lián)對象的標(biāo)志位迹冤。
refs存儲當(dāng)前對象的所有關(guān)聯(lián)對象
- associations已經(jīng)分配時,通過key進行存儲
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);//key已經(jīng)存在時敛瓷,找到對應(yīng)位置更新舊的
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);//key沒有存儲時叁巨,直接存儲
}
如果key存儲過值,找到對應(yīng)的value的位置呐籽,替換舊的锋勺。
如果key沒有,ObjcAssociation構(gòu)造的對象狡蝶。
關(guān)聯(lián)對象存儲分三種情況:
第一種:對象第一次使用關(guān)聯(lián)對象初始化庶橱,ObjectAssociationMap,使用策略和value構(gòu)造存儲對象
第二種:對象的關(guān)聯(lián)對象已經(jīng)初始化贪惹,在ObjectAssociationMap查找苏章,沒找到按key直接存儲。
第三種:對象的關(guān)聯(lián)對象已經(jīng)初始化奏瞬,在ObjectAssociationMap查找枫绅,找到按key替換原有的存儲。
2.2 objc_getAssociatedObject
id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
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;
}
這一段在associations查找硼端,如果策略是OBJC_ASSOCIATION_GETTER_RETAIN并淋,會有個計數(shù)+1objc_retain(value);如果是OBJC_ASSOCIATION_GETTER_AUTORELEASE,通過objc_autorelease延遲釋放珍昨。
2.3objc_removeAssociatedObjects
void objc_removeAssociatedObjects(id object)
{
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()) {
// copy all of the associations that need to be removed.
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
elements.push_back(j->second);
}
// remove the secondary table.
delete refs;
associations.erase(i);//刪除
}
}
// the calls to releaseValue() happen outside of the lock.
for_each(elements.begin(), elements.end(), ReleaseValue());
}
這個方法在dealloc中通過hasAssociatedObjects標(biāo)志位釋放县耽,
找到關(guān)聯(lián)對象elements,遍歷進行釋放镣典。
總結(jié):
1.不用修改原有對象就能為其添加屬性兔毙,不消耗對象分配空間。
2.關(guān)聯(lián)對象釋放不用擔(dān)心兄春,系統(tǒng)會在對象銷毀時在dealloc中根據(jù)標(biāo)志位進行釋放澎剥。
3.關(guān)聯(lián)對象與分類的關(guān)系?關(guān)聯(lián)對象很好的解決了分類不能添加屬性的問題赶舆。
4.關(guān)聯(lián)對象的本質(zhì):由AssociationsManager管理并在AssociationsHashMap存儲哑姚。
所有對象的關(guān)聯(lián)內(nèi)容都在同一個全局容器中趾唱。