提问
- 关联对象被存储在什么地方,是不是存放在被关联对象本身的内存中?
- 为什么 AssociationsHashMap 是使用
unordered_map
,而 ObjectAssociationMap 使用 map
- 使用关联对象有什么需要注意的地方
- 为什么内存管理策略中没有 weak 选项,即
OBJC_ASSOCIATION_WEAK
阅读本文的前提
1
| objc_setAssociatedObject(self, @selector(vn_cellCollectionView), proxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
其中 self 我们称之为被关联对象,proxy 我们称之为 关联对象
关键数据结构
理解好以下几个结构之后,几乎不需要看源码就能推测 set/get 的原理
AssociationsManager
单例,全局唯一
AssociationsHashMap
AssociationsManager 的属性,全局唯一
std::unordered_map
,存放对象与 ObjectAssociationMap(这里的对象指的是添加属性的分类)
ObjectAssociationMap
std::map
,存放 key 与 ObjcAssociation
ObjcAssociation
存放 value 和 policy
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) { 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) { 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; 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); _class_setInstancesHaveAssociatedObjects(_object_getClass(object)); } } else { 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); } } } }
if (old_association.hasValue()) ReleaseValue()(old_association); }
|
1 2 3 4 5 6 7 8 9 10 11
| static id acquireValue(id value, uintptr_t policy) { switch (policy & 0xFF) { case OBJC_ASSOCIATION_SETTER_RETAIN: return ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain); case OBJC_ASSOCIATION_SETTER_COPY: return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy); } return value; }
|
1 2
| inline disguised_ptr_t DISGUISE(id value) { return ~uintptr_t(value); }
|
解答
关联对象被存储在什么地方,是不是存放在被关联对象本身的内存中?
关联对象并不是存放在被关联对象本身的内存中,而是存放在一个全局的 unordered_map
中
其中 unordered_map
存储着被关联的对象与 map
,其中 map 存放着关联对象的 key 和关联对象
获取关联对象时,首先通过被关联对象的地址找到 map
,然后通过关联对象的 key 找到关联对象并返回
为什么 AssociationsHashMap 是使用 unordered_map
,而 ObjectAssociationMap 使用 map
unordered_map
查找更加高效,大多数情况下应该选择 unordered_map
,而 ObjectAssociationMap 使用 map
应该是出于内存考虑
详细可参考 unordered_map
与 map
的区别
更多阅读