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

2018-07-03  本文已影响0人  LNG61

一、关联对象

之前在分类(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>来实现。
上一篇下一篇

猜你喜欢

热点阅读