585 字
3 分钟
关联对象源码解析
前言
为分类添加关联对象是我们比较常用的,但是你真的了解关联对象么?
本文基于
objc4-818.2源码
一、 关联对象
由于分类的属性不会生产相关的成员变量,所以我们需要自己实现 set/get 方法。如下:
@property (nonatomic, copy) NSString *cateName;
// .m- (void)setCateName:(NSString *)cateName { objc_setAssociatedObject(self, "cateName", cateName, OBJC_ASSOCIATION_COPY_NONATOMIC);}
- (NSString *)cateName { return objc_getAssociatedObject(self, "cateName");}那么,objc_setAssociatedObject 和 objc_getAssociatedObject,究竟做了什么呢?
二、 objc_setAssociatedObject

实际调用了 _object_set_associative_reference

2.1 AssociationsHashMap

通过断点我们发现了一个 AssociationsHashMap。

可以看出它是一个 全局静态 的 Storage 类型的 HashMap。用来保存运行时所有的关联对象的关联关系。
2.2 try_emplace
如果表中没有的话,就将键值对插入表中。
Inserts key,value pair into the map if the key isn’t already in the map. The value is constructed in-place if the key is not in the map, otherwise it is not moved.

一次 set 两次调用了 try_emplace
通过代码可以看出,一次set关联值,会调用两次 try_emplace。

auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});- 只用来检查是否存在关联对象的
keyObjectAssociationMap{}是个空的不做处理
- 只用来检查是否存在关联对象的
auto result = refs.try_emplace(key, std::move(association));- 这里才是真的设置
value
- 这里才是真的设置
触发set
通过调用 LookupBucketFor 查找表中是否已经存在了对应的关联对象。
第一次调用set方法

第二次调用set方法

2.3 LookupBucketFor
和查找方法缓存的算法基本一致。
2.4 InsertIntoBucket
插入表中
template <typename KeyArg, typename... ValueArgs>BucketT *InsertIntoBucket(BucketT *TheBucket, KeyArg &&Key, ValueArgs &&... Values) {TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket);
// first 就是 keyTheBucket->getFirst() = std::forward<KeyArg>(Key);
// second 是Value::new (&TheBucket->getSecond()) ValueT(std::forward<ValueArgs>(Values)...);return TheBucket;}三、 关联对象释放
思考:在对象
dealloc的时候,会自动清理管理那对象么?
3.1 dealloc
我们从 dealloc 开始找:
- dealloc
- _objc_rootDealloc
- rootDealloc
- 如果有关联对象
- object_dispose
- objc_destructInstance
- _object_remove_assocations
- 来释放关联对象
- _object_remove_assocations
- objc_destructInstance
- rootDealloc
- _objc_rootDealloc
void_object_remove_assocations(id object, bool deallocating){ ObjectAssociationMap refs{};
{ AssociationsManager manager; AssociationsHashMap &associations(manager.get()); AssociationsHashMap::iterator i = associations.find((objc_object *)object); if (i != associations.end()) { refs.swap(i->second);
// If we are not deallocating, then SYSTEM_OBJECT associations are preserved. bool didReInsert = false; if (!deallocating) { for (auto &ref: refs) { if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) { i->second.insert(ref); didReInsert = true; } } } if (!didReInsert) associations.erase(i); } }
// Associations to be released after the normal ones. SmallVector<ObjcAssociation *, 4> laterRefs;
// release everything (outside of the lock). for (auto &i: refs) { if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) { // If we are not deallocating, then RELEASE_LATER associations don't get released. if (deallocating) laterRefs.append(&i.second); } else { i.second.releaseHeldValue(); } } for (auto *later: laterRefs) { later->releaseHeldValue(); }}