为了更好的活着Objective-C RunTime

OC关联对象小结(一)

2016-03-21  本文已影响1274人  comst

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);
}

代码中涉及到的一些数据结构。

  1. AssociationsManager 是顶级的对象,维护了一个从 spinlock_t 锁到 AssociationsHashMap 哈希表的单例键值对映射;
  2. AssociationsHashMap 是一个无序的哈希表,维护了从对象地址到 ObjectAssociationMap 的映射;
  3. ObjectAssociationMap 是一个 C 中的 map ,维护了从 key 到 ObjcAssociation 的映射,即关联记录;
  4. ObjcAssociation 是一个 C 的类,表示一个具体的关联结构,主要包括两个实例变量,_policy 表示关联策略,_value 表示关联对象。
    流程图如下:



    看懂这个,那其他的几个也就懂了。

上一篇下一篇

猜你喜欢

热点阅读