美文网首页
九、关联对象(AssociatedObject)的实现与应用

九、关联对象(AssociatedObject)的实现与应用

作者: LNG61 | 来源:发表于2018-07-03 20:18 被阅读0次

    一、关联对象

    之前在分类(Category)的结构与加载中提到过,分类中声明的property不能自动生成变量,需要手动去实现,这个时候可以借用关联对象来实现。

    二、关联对象的实现

    接下来看一下关联对象是怎样实现的,关联对象主要通过以下三个函数来获取、设置和删除。

    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
    id objc_getAssociatedObject(id object, const void *key);
    void objc_removeAssociatedObjects(id object);
    

    1. objc_AssociationPolicy

    objc_AssociationPolicy是存取策略,与property的修饰符相对应,以下是对应关系:

    objc_AssociationPolicy property
    OBJC_ASSOCIATION_ASSIGN assign
    OBJC_ASSOCIATION_RETAIN_NONATOMIC strong, nonatomic
    OBJC_ASSOCIATION_COPY_NONATOMIC copy,nonatomic
    OBJC_ASSOCIATION_RETAIN strong,atomic
    OBJC_ASSOCIATION_COPY copy,atomic

    2.objc_setAssociatedObject

    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy){
      AssociationsManager manager;
      AssociationsHashMap &associations(manager.associations));
      disguised_ptr_t disguised_object = DISGUISE(object);
      if(value){
        // 存在新值
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if(i != associations.end()){
            // 该key对应的ObjectAssociationMap已经存在
            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{
            //  创建key对应的ObjectAssociationMap
            ObjectAssociationMap *refs = new ObjectAssociationMap ;
            associations[disguised_object] = refs;
            (*refs)[key] = ObjcAssociation(policy, new_value);
            object->setHasAssociatedObjects();
        }
      }else{
          // 将key对应的ObjectAssociationMap从AssociationsHashMap移除
      }
    
       ...
    }
    

    这里出现了几个新的数据结构,AssociationsManagerAssociationsHashMapObjectAssociationMap
    (1)AssociationsManagerAssociationsHashMap的单例。
    (2)AssociationsHashMapunordered_map<disguised_ptr_t, ObjectAssociationMap *>的哈希表。
    (3)ObjectAssociationMapmap<void *, ObjcAssociation>的容器。
    (4)ObjcAssociation定义如下,保存着值和存取策略:

    class ObjcAssociation{
        uintptr_t policy;
        id _value;
    };
    

    这样的话就比较明确了,通过以下方式来保存着一个ObjcAssociation对象。
    AssociationsManager->AssociationsHashMap[DISGUISE(object)]->ObjectAssociationMap [key]->ObjcAssociation
    objc_setAssociatedObject的逻辑也比较简单,如果没有对应的ObjectAssociationMap就重新插入一个,如果已经有了则赋新值,并将原来的释放掉,如果设置为nil则是删掉对应的ObjectAssociationMap

    3.objc_getAssociatedObject

    id objc_getAssociatedObject(id object, const void *key){
      ...
      AssociationsManager manager;
      AssociationsHashMap &associations(manager.associations));
      disguised_ptr_t disguised_object = DISGUISE(object);
      AssociationsHashMap::iterator i = associations.find(disguised_object);
      if(i != associations.end()){
          ObjectAssociationMap *refs = i->second;
          ObjectAssociationMap::iterator j = refs->find(key);
          if(j != ref->end()){
            ObjcAssociation &entry = j->second;
            value= entry.value();
          }
      }
      ...
      return value;
    }
    

    objc_getAssociatedObject的逻辑就完全按照我们第2步猜想的方法去找这个key和object对应的值了。

    4.objc_removeAssociatedObjects

    objc_removeAssociatedObjects的逻辑就是找到object对应的AssociationsHashMap,然后将里面所有的ObjcAssociation对象进行一一释放。

    小结:

    1. 由于分类中无法添加变量,关联对象可用于分类category属性的实现。
    2. 关联对象是用其它存储空间辅助实现的,用了一个hashmap<object>和map<key>来实现。

    相关文章

      网友评论

          本文标题:九、关联对象(AssociatedObject)的实现与应用

          本文链接:https://www.haomeiwen.com/subject/kkbjuftx.html