分类属性存取之关联对象

469 阅读3分钟

引入

我们知道,分类可以添加为类添加方法,通过 @property也可以定义一个变量,但只会生成变量的gettersetter方法的声明,没有方法的实现,也没有带下划线的成员变量。所以gettersetter方法的实现需要我们自己去实现。我们使用runtime为类添加关联对象,会调用到两个runtime的函数objc_setAssociatedObjectobjc_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包装成一个DisguisedPtr
  • ObjcAssociation association{policy, value};将存储策略policyvalue值包装成一个ObjcAssociation

acquireValue函数是对不同存储策略下的处理

  • OBJC_ASSOCIATION_SETTER_RETAIN进行objc_retain;

AssociationsManager 是一个关联对象管理类

  • 构造函数析构函数中加锁,防止多线程重复创建导致的对HashMap的重复操作

AssociationsHashMap 是一个存储关联对象的全局的HashMap表

  • 1.AssociationsHasMap的结构是一个以DisguisedPtr为key和ObjcetAssociationMap为value的键值对;
  • 2.AssociationsHashMap是通过AssociationsManagerget函数创建的;
  • 3.static Storage _mapStorage保证了AssociationsHashMap的全局唯一.

  • value存在,则通过函数try_emplace根据装配的disguisedhashMap表中查找,找到后插入到表中;
  • value不存在,则通过函数find根据装配的disguisedhashMap表中查找,然后将表中原有的记录抹去。

  • 没找到就返回一个空的BucketT,方便外部调用者设置新值;
  • 找到了返回外界调用者设置新值。

关联对象结构

AssociationsHashMap是以DisguisedPtr为key,ObjectAssociationMap为Value的HashMap表,每个使用到了关联对象的类都会被插入到这个HashMap中; ObjectAssociationMap是以const void*类型的数据为key,以ObjcetAssociation为value的Map表,使用到关联对象的类的实例的关联对象都会存在这个Map表中。

设值流程总结

    1. 创建一个AssociationsManager管理类;
    1. 获取唯一的全局静态HashMap;
    1. 判断插入的关联值是否存在: (1)存在走第4步; (2)不存在就走 : 关联对象插入空流程;
    1. 创建一个空的ObjectAssociationMap去取查询的键值对;
    1. 如果发现没有这个key就插入一个空的BucketT进去,返回;
    1. 标记对象存在关联对象;
    1. 用当前存储策略value组成了一个ObjcAssociation替换原来BucketT中的空;
    1. 标记一下ObjectAssociationMap的第一次为false.

关联对象插入空流程

    1. 根据DisguisedPtr找到AssociationsHashMap中的iterator迭代查询器;
    1. 清理迭代器;
    1. 其实如果插入空置相当于清除.

取值分析

objc_getAssociatedObject分析

_object_get_associative_reference分析

  • 创建AssociationsManager,并找到全局唯一的AssociationsHashMap
  • 根据Disguised找到AssociationsHashMap的迭代器iterator;
  • 找到objcetObjectAssociationMap
  • 根据key找到ObjectAssociationMap中的value,并返回。

取值流程总结

  • 1.创建一个AssociationsManager管理类;
  • 2.获取唯一的全局静态HashMap;
  • 3.根据DisguisedPtr找到AssociationsHashMap中的 iterator 迭代查询器;
  • 4.如果这个迭代查询器不是最后一个,获取ObjectAssociationMap (这里有存储策略和value);
  • 5.找到ObjectAssociationMap迭代查询器获取一个经过属性修饰符修饰的value;
  • 6.返回_value.

总结

从关联对象的存取值分析可以看出,关联对象其实就是维护了一个两层的哈希Map表。