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

一、关联对象

之前在分类(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移除
  }

   ...
}

这里出现了几个新的数据结构,AssociationsManager、AssociationsHashMap和ObjectAssociationMap。
(1)AssociationsManager是AssociationsHashMap的单例。
(2)AssociationsHashMap是unordered_map<disguised_ptr_t, ObjectAssociationMap *>的哈希表。
(3)ObjectAssociationMap是map<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>来实现。
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容