关联对象的实现
介绍
我们在开发中都使用过category,并且都了解category不能添加属性,因为在runtime时候,类的成员变量已经固定,并且categery的底层实现中没有设置存放成员变量的逻辑,所以无法给category添加属性,如果要添加属性需要使用关联对象来处理
- 添加
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
- 获取
objc_getAssociatedObject(id object, const void *key);
- 移除
objc_removeAssociatedObjects(id object)
objc_setAssociatedObject的实现
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
下面我们仔细看一下_object_set_associative_reference(object, (void *)key, value, policy)
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
if (!object && !value) return;
assert(object);
if (object->getIsa()->forbidsAssociatedObjects())
_objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
// 创建一个 ObjcAssociation对象,初始化policy为OBJC_ASSOCIATION_ASSIGN,value为nil。
ObjcAssociation old_association(0, nil);//(1)
//如果value有值,判断策略相应处理赋值给new_value
id new_value = value ? acquireValue(value, policy) : nil;//(2)
{ //c++默认初始化对象
AssociationsManager manager;//(3)
//获取全局的AssociateiionsMap
AssociationsHashMap &associations(manager.associations());//(4)
//将对象转化为disguised_ptr_t类型的对象
disguised_ptr_t disguised_object = DISGUISE(object);//(5)
if (new_value) {
//通过AssociationsHashMap的迭代器,查找object对应的ObjectAssociationMap
AssociationsHashMap::iterator i = associations.find(disguised_object);(6)
if (i != associations.end()) {
// secondary table exists,找到object对应的关联对象表
ObjectAssociationMap *refs = i->second;
//再使用迭代器,查找外部参数key所对应的关联属性
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
//保存旧关联,用于最后的释放
old_association = j->second;
//设置新的关联值
j->second = ObjcAssociation(policy, new_value);
} else {
//找不到key的关联属性,使用构造函数新增一个关联属性
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
//object第一次添加关联对象,则为object创建关联表ObjectAssociationMap
ObjectAssociationMap *refs = new ObjectAssociationMap
//将ObjectAssociationMap添加进AssociationsHashMap中
associations[disguised_object] = refs;
//添加关联属性
(*refs)[key] = ObjcAssociation(policy, new_value);
//设置object的isa中的has_assoc值为true,表示有关联对象,用于后期对象的释放等操作
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
//value为nil,释放object中与key有关的关联属性
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).释放旧的关联对象
if (old_association.hasValue()) ReleaseValue()(old_association);
}
(1)ObjcAssociation:关联对象的结构,存储着关联策略_policy
和关联对象的值_value
。
class ObjcAssociation {
uintptr_t _policy;
id _value;
public:
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
ObjcAssociation() : _policy(0), _value(nil) {}
uintptr_t policy() const { return _policy; }
id value() const { return _value; }
bool hasValue() { return _value != nil; }
};
(2)acquireValue
static id acquireValue(id value, uintptr_t policy) {
switch (policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
return objc_retain(value);
case OBJC_ASSOCIATION_SETTER_COPY:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
}
return value;
}
复制代码
判断引用策略,做相应处理,只处理了retain和copy两种
- 如果为Retian的,调用
objc_retain
将value的引用计数加一 - 如果是Copy,调用Value的copy方法生成新的拷贝。
(3)AssociationsManager
spinlock_t AssociationsManagerLock;
class AssociationsManager {
// associative references: object pointer -> PtrPtrHashMap.
static AssociationsHashMap *_map;
public:
//初始化AssociationsManager的时候,调用lock.lock()方法
AssociationsManager() { AssociationsManagerLock.lock(); }
//析构的时候调用lock.unlock()
~AssociationsManager() { AssociationsManagerLock.unlock(); }
//associations()方法获取全局的AssociationsHashMap单例
AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
AssociationsHashMap *AssociationsManager::_map = NULL;
AssociationsManager
通过持有一个自旋锁spinlock_t
保证对 AssociationsHashMap
的操作是线程安全的,即每次只会有一个线程对 AssociationsHashMap 进行操作。
(4)AssociationsHashMap
ObjectAssociationMap
保存了从 key
到关联对象 ObjcAssociation
的映射,这个数据结构保存了当前对象对应的所有关联对象,是一张保存了与对象有关的所有的关联属性的表。key是代码中定义的key,value是ObjcAssociation
。
AssociationsHashMap
用与保存从对象的 disguised_ptr_t
到 ObjectAssociationMap
的映射。其中key是 disguised_ptr_t
,value是 ObjectAssociationMap
typedef ObjcAllocator<std::pair<void * const, ObjcAssociation> > ObjectAssociationMapAllocator;
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
typedef ObjcAllocator<std::pair<const disguised_ptr_t, ObjectAssociationMap*> > AssociationsHashMapAllocator;
class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
上面的代码中包含其他结构,我们来看一下
【1】ObjectAssociationMap:Key是void * const(内存地址),Value是ObjcAssociation
它继承自std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>
泛型结构,泛型定义如下
std::map的泛型定义
template <class _Key, class _Tp,class _Compare = less<_Key>, class _Allocator = allocator<pair<const _Key, _Tp> > >
我们分析一下std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>
结构
- _Key:void *,即无类型指针,表示内存地址。
- _Tp:ObjcAssociation,用来存放关联对象的值与关联策略。
- _Compare:ObjectPointerLess,提供比较算法(比对指针地址)。
- _Allocator:ObjectAssociationMapAllocator,它的类型为ObjcAllocator<std::pair<void * const, ObjcAssociation> >,用std::pair实现的键值对,Key是void * const(内存地址)是设置关联对象的key的内存地址,Value是ObjcAssociation
std::pair主要的作用是将两个数据组合成一个数据,两个数据可以是同一类型或者不同类型。例如std::pair<int,float> 或者 std::pair<double,double>等。初始化一个pair可以使用构造函数,也可以使用std::make_pair函数
【2】AssociationsHashMap:Key是disguised_ptr_t,Value是ObjectAssociationMap *的键值对。
它继承自unordered_map <disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator>
,解释下这个泛型的意思
-
_Key(key_type):disguised_ptr_t,disguised_ptr_t是unsigned long类型,用来存放指针的。
-
_Tp(mapped_type):ObjectAssociationMap *,代表Map中存储的值类型。
-
_Hash(hasher):DisguisedPointerHash,提供hash算法。
-
_Pred(key_equal):DisguisedPointerEqual,提供equal算法(两个指针相同则相等)。
-
_Alloc(allocator_type):构造函数的类型,这里构造函数类型是ObjcAllocator<std::pair<const disguised_ptr_t, ObjectAssociationMap *> >,也是一个泛型,用std::pair实现Key是disguised_ptr_t,Value是ObjectAssociationMap *的键值对。
(5)disguised_ptr_t 与DISGUISE函数
typedef unsigned long uintptr_t;
typedef uintptr_t disguised_ptr_t;
inline disguised_ptr_t DISGUISE(id value) { return ~uintptr_t(value); }
inline id UNDISGUISE(disguised_ptr_t dptr) { return id(~dptr); }
复制代码
-
disguised_ptr_t
实际上是一个unsigned long,它的长度与指针相同,所以被当做指针使用。 -
DISGUISE(id value)用来将对象转化为disguised_ptr_t类型的对象 此函数返回
~uintptr_t(value)
,value是id,也就是是对象指针,将它包装成unsigned long类型,载逐位取反后返回。 -
UNDISGUISE(disguised_ptr_t dptr)执行DISGUISE的反操作返回
id(~dptr)
,将disguised_ptr_t逐位取反后,返回对象(id)指针。
实践并总结
如果我们给一个分类的对象obj添加一个Hello属性,初始值是Hello,代码如下例如
objc_setAssociatedObject(obj, @selector(hello), @"Hello", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
对应的关联对象 为ObjcAssociation(OBJC_ASSOCIATION_RETAIN_NONATOMIC, @"Hello")
它在内存中的存储如下
objc_getAssociatedObject的实现
id objc_getAssociatedObject(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
下面我们看看_object_get_associative_reference()
的实现
id _object_get_associative_reference(id object, void *key) {
//创建初始值为nil的value,默认引用策略为assign
id value = nil;
uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
{
//创建manager,加锁
AssociationsManager manager;
//获取全局的AssociateiionsMap
AssociationsHashMap &associations(manager.associations());
//将对象转化为disguised_ptr_t类型的disguised_object对象
disguised_ptr_t disguised_object = DISGUISE(object);
//在全局的AssociationsMap中根据disguised_object查找对应的ObjectAssociationMap
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
//获取objectAssociationMap
ObjectAssociationMap *refs = i->second;
//在objectAssociationMap中根据key查找ObjectAssociation
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
ObjcAssociation &entry = j->second;
//从ObjectAssociation中获取value和policy
value = entry.value();
policy = entry.policy();
// 如果引用策略是Retain模式
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
// 调用value的retian方法,引用计数加一。
objc_retain(value);
}
}
}
}
// 如果value存在,并且引用策略是AutoRelease模式。
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
// 调用value的autorelease方法,将value设置为autorelease。
objc_autorelease(value);
}
return value;
}
objc_removeAssociatedObjects的实现
void objc_removeAssociatedObjects(id object)
{
if (object && object->hasAssociatedObjects()) {
_object_remove_assocations(object);
}
}
下面我们看看_object_remove_assocations()
的实现
void _object_remove_assocations(id object) {
vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
{
//获取manager,内部加锁
AssociationsManager manager;
//获取全局的AssociationHashMap
AssociationsHashMap &associations(manager.associations());
//如果没有全局的AssociationHashMap,并表示没有关联属性,流程结束
if (associations.size() == 0) return;
//在AssociationsHashMap中查找disguised_object对应的ObjectAssociationMap。
disguised_ptr_t disguised_object = DISGUISE(object);
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
//获取objectAssciationMap
ObjectAssociationMap *refs = i->second;
for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
//将所有的ObjectAssociation都存入vector中。
elements.push_back(j->second);//(1)
}
//移除objectAssociationMap二级表
delete refs;
//释放该二级表在全局associationHashMap中的空间
associations.erase(i);
}
}
// 循环释放vector中保存的每一个ObjectAssociation
for_each(elements.begin(), elements.end(), ReleaseValue());
}
(1)push_back()
push_back()是箱vector的尾部添加元素,我们看一下什么是vector。
vector是和array类似的容器,不过相对于array,vector具有动态性,即在元素的插入和删除过程中,vector自身回去调整所占空间,不需认为干预。
- begin():返回指向容器中第一个元素的迭代器。
- end():返回指向容器最后一个元素所在位置后一个位置的迭代器,通常和 begin() 结合使用