一、关联对象
之前在分类(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
对象进行一一释放。
小结:
- 由于分类中无法添加变量,关联对象可用于分类
category
属性的实现。 - 关联对象是用其它存储空间辅助实现的,用了一个hashmap<object>和map<key>来实现。
网友评论