OC关联对象小结(一)
使用场景
为现有的类添加属性,变量
在Objective-C中可以通过Category给一个现有的类添加属性(如NSObject),但是却不能添加实例变量,然而可以通过Associated Object间接地达到这一目的。示例代码展示了给NSObject添加实例变量。
为KVO
创建一个关联的观察者
相关函数
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);
objc_setAssociatedObject
用于给对象添加关联对象,传nil
可以移除相关的关联对象。
objc_getAssocicatedObject
用于获取关联对象的值。
objc_removeAssociatedObject
用于移除该对象的所有关联对象。如果打算只移除一部分则不能使用该方法。
相关参数
key:要保证全局唯一,key与关联的对象是一一对应关系。必须全局唯一。通常用@selector(methodName)
作为key。
value:要关联的对象。
policy:关联策略。有五种关联策略。
OBJC_ASSOCIATION_ASSIGN
等价于 @property(assign)
。
OBJC_ASSOCIATION_RETAIN_NONATOMIC
等价于 @property(strong, nonatomic)
。
OBJC_ASSOCIATION_COPY_NONATOMIC
等价于@property(copy, nonatomic)
。
OBJC_ASSOCIATION_RETAIN
等价于@property(strong,atomic)
。
OBJC_ASSOCIATION_COPY
等价于@property(copy, atomic)
。
原理简介
运行时通过map维系一张关联对象与被关联对象之间的关系。
objc_setAssociatedObject
的相关代码.
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
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 {
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
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);
}
代码中涉及到的一些数据结构。
- AssociationsManager 是顶级的对象,维护了一个从 spinlock_t 锁到 AssociationsHashMap 哈希表的单例键值对映射;
- AssociationsHashMap 是一个无序的哈希表,维护了从对象地址到 ObjectAssociationMap 的映射;
- ObjectAssociationMap 是一个 C 中的 map ,维护了从 key 到 ObjcAssociation 的映射,即关联记录;
-
ObjcAssociation 是一个 C 的类,表示一个具体的关联结构,主要包括两个实例变量,_policy 表示关联策略,_value 表示关联对象。
流程图如下:
看懂这个,那其他的几个也就懂了。
网友评论