引入
我们知道,分类可以添加为类添加方法,通过 @property也可以定义一个变量,但只会生成变量的getter和setter方法的声明,没有方法的实现,也没有带下划线的成员变量。所以getter和setter方法的实现需要我们自己去实现。我们使用runtime为类添加关联对象,会调用到两个runtime的函数objc_setAssociatedObject和objc_getAssociatedObject.通过两两个函数,就可以将分类中定义的变量的数据存储到类的关联对象的表中。那么是如何存储和取值的呢?
存储分析
我们通过一份objc4-781 源码来分析
objc_setAssociatedObject底层调用
SetAssocHook.get()是一个函数指针的调用,其实际调用的函数是_base_objc_setAssociatedObject
_base_objc_setAssociatedObject调用的是_object_set_associative_reference函数
也就是说
objc_setAssociatedObject实际调用的是_object_set_associative_reference,那么为什么要中间加这么多层的调用呢?一是可以方便后续进行更多的设置,二是可以更灵活的在多处进行不同设置的调用。
_object_set_associative_reference分析
DisguisedPtr<objc_object> disguised{(objc_object *)object};将objcet包装成一个DisguisedPtrObjcAssociation association{policy, value};将存储策略policy和value值包装成一个ObjcAssociation
acquireValue函数是对不同存储策略下的处理
- 对
OBJC_ASSOCIATION_SETTER_RETAIN进行objc_retain;
AssociationsManager 是一个关联对象管理类
- 在
构造函数和析构函数中加锁,防止多线程重复创建导致的对HashMap的重复操作
AssociationsHashMap 是一个存储关联对象的全局的HashMap表
- 1.
AssociationsHasMap的结构是一个以DisguisedPtr为key和ObjcetAssociationMap为value的键值对;- 2.
AssociationsHashMap是通过AssociationsManager的get函数创建的;- 3.
static Storage _mapStorage保证了AssociationsHashMap的全局唯一.
value存在,则通过函数try_emplace根据装配的disguised去hashMap表中查找,找到后插入到表中;value不存在,则通过函数find根据装配的disguised去hashMap表中查找,然后将表中原有的记录抹去。
- 没找到就返回一个空的BucketT,方便外部调用者设置新值;
- 找到了返回外界调用者设置新值。
关联对象结构
AssociationsHashMap是以DisguisedPtr为key,ObjectAssociationMap为Value的HashMap表,每个使用到了关联对象的类都会被插入到这个HashMap中;ObjectAssociationMap是以const void*类型的数据为key,以ObjcetAssociation为value的Map表,使用到关联对象的类的实例的关联对象都会存在这个Map表中。
设值流程总结
- 创建一个
AssociationsManager管理类;
- 获取唯一的全局静态
HashMap;
- 判断插入的关联值是否存在: (1)存在走第4步; (2)不存在就走 :
关联对象插入空流程;
- 创建一个空的
ObjectAssociationMap去取查询的键值对;
- 如果发现没有这个
key就插入一个空的BucketT进去,返回;
- 标记
对象存在关联对象;
- 用当前
存储策略和value组成了一个ObjcAssociation替换原来BucketT中的空;
- 标记一下
ObjectAssociationMap的第一次为false.
关联对象插入空流程
- 根据
DisguisedPtr找到AssociationsHashMap中的iterator迭代查询器;
- 清理迭代器;
- 其实如果插入空置相当于清除.
取值分析
objc_getAssociatedObject分析
_object_get_associative_reference分析
- 创建
AssociationsManager,并找到全局唯一的AssociationsHashMap;- 根据
Disguised找到AssociationsHashMap的迭代器iterator;- 找到
objcet的ObjectAssociationMap;- 根据
key找到ObjectAssociationMap中的value,并返回。
取值流程总结
- 1.创建一个
AssociationsManager管理类;- 2.获取唯一的全局静态
HashMap;- 3.根据
DisguisedPtr找到AssociationsHashMap中的iterator迭代查询器;- 4.如果这个迭代查询器不是最后一个,获取
ObjectAssociationMap(这里有存储策略和value);- 5.找到
ObjectAssociationMap的迭代查询器获取一个经过属性修饰符修饰的value;- 6.返回
_value.
总结
从关联对象的存取值分析可以看出,关联对象其实就是维护了一个两层的哈希Map表。