18.关联对象源码解析

前言

为分类添加关联对象是我们比较常用的,但是你真的了解关联对象么?

本文基于 objc4-818.2 源码

一、 关联对象

由于分类的属性不会生产相关的成员变量,所以我们需要自己实现 set/get 方法。如下:

// .h

@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_setAssociatedObjectobjc_getAssociatedObject,究竟做了什么呢?

二、 objc_setAssociatedObject

objc_setAssociatedObject

实际调用了 _object_set_associative_reference

_object_set_associative_reference

2.1 AssociationsHashMap

AssociationsHashMap

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

AssociationsManager

可以看出它是一个 全局静态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.

try_emplace

一次 set 两次调用了 try_emplace

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

try_emplace
  • auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});

    • 只用来检查是否存在关联对象的 key ObjectAssociationMap{} 是个空的不做处理

  • auto result = refs.try_emplace(key, std::move(association));

    • 这里才是真的设置 value

触发set

通过调用 LookupBucketFor 查找表中是否已经存在了对应的关联对象。

第一次调用set方法

第一次调用set方法

第二次调用set方法

第二次调用set方法

2.3 LookupBucketFor

和查找方法缓存的算法基本一致。

2.4 InsertIntoBucket

插入表中

三、 关联对象释放

思考:在对象 dealloc 的时候,会自动清理管理那对象么?

3.1 dealloc

我们从 dealloc 开始找:

  • dealloc

    • _objc_rootDealloc

      • rootDealloc

        • 如果有关联对象

        • object_dispose

          • objc_destructInstance

            • _object_remove_assocations

              • 来释放关联对象

Last updated

Was this helpful?