您的位置:首页 > 移动开发 > Objective-C

objective-c中关联引用的底层实现

2016-05-25 15:04 489 查看
我们知道关联引用(associative references)可以为已存在的实例对象增加实例变量。该技术主要由以下三个函数实现相应的功能:

1. void objc_setAssociatedObject(id object, void *key, id value,objc_AssociationPolicy policy)

这个方法用于为对象object添加以key指定的地址作为关键字,以value为值的关联引用,第四个参数policy指定关联引用的存储策略。

通过将value置为nil就可以删除对应key的关联。

2. id objc_getAssociatedObject(id object, void *key)

返回对象object以key为关键字的关联对象。如果没有关联对象则返回nil。

3. void objc_removeAssociatedObjects(id object)

断开object的所有关联。

至于以上函数的具体用法就不多说了,相信来看原理的客官们已经都会了( ^_^ )

下面是一些源码中会用到的结构体和函数定义,不想太在意细节的可以直接略过,也可以在后面遇到的时候回过头来看:

enum {
OBJC_ASSOCIATION_SETTER_ASSIGN      = 0,
OBJC_ASSOCIATION_SETTER_RETAIN      = 1,
OBJC_ASSOCIATION_SETTER_COPY        = 3,            // NOTE:  both bits are set, so we can simply test 1 bit in releaseValue below.
OBJC_ASSOCIATION_GETTER_READ        = (0 << 8),
OBJC_ASSOCIATION_GETTER_RETAIN      = (1 << 8),
OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
};

enum {
OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
*   The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied.
*   The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
*   The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
*   The association is made atomically. */
};

inline disguised_ptr_t DISGUISE(id value) { return ~uintptr_t(value); }
inline id UNDISGUISE(disguised_ptr_t dptr) { return id(~dptr); }

class AssociationsManager {
static spinlock_t _lock;
static AssociationsHashMap *_map;               // associative references:  object pointer -> PtrPtrHashMap.
public:
AssociationsManager()   { spinlock_lock(&_lock); }
~AssociationsManager()  { spinlock_unlock(&_lock); }

AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};

spinlock_t AssociationsManager::_lock = SPINLOCK_INITIALIZER;
AssociationsHashMap *AssociationsManager::_map = NULL;

class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>

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; }
};
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
}
return value;
}

static id acquireValue(id value, uintptr_t policy) {
switch (policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
case OBJC_ASSOCIATION_SETTER_COPY:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
}
return value;
}

static void releaseValue(id value, uintptr_t policy) {
if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
((id(*)(id, SEL))objc_msgSend)(value, SEL_release);
}
}

struct ReleaseValue {
void operator() (ObjcAssociation &association) {
releaseValue(association.value(), association.policy());
}
};


进入第一个函数(setAssociatedObject)的最终实现:(因为保存为代码的时候总是有莫名的字符添加上去,所以就直接贴出来了)

void_object_set_associative_reference(id object, void *key, id value, uintptr_tpolicy) {

// retain the new value (if any) outside the lock.

ObjcAssociation old_association(0, nil);
//构造了一个表示旧值的对象

//根据相应的策略做retain或copy操作,见acquireValue函数

id new_value = value ? acquireValue(value,policy) : nil;

{

AssociationsManager manager;

//获得全局map对象的引用

AssociationsHashMap&associations(manager.associations());

disguised_ptr_t disguised_object =DISGUISE(object);

//如果新值不为空

if (new_value) {

// break any existing association.

//找到对应object的关联map容器

AssociationsHashMap::iterator i =associations.find(disguised_object);

if (i != associations.end()) {

// secondary table exists

//取得关联的map

ObjectAssociationMap *refs =i->second;

//取得关联的具体值

ObjectAssociationMap::iteratorj = 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 {//该object是第一次关联对象,新建一个map容器用于保存它的关联map

// create the new association (first time).

ObjectAssociationMap *refs =new ObjectAssociationMap;

//将该值设置在全局map中

associations[disguised_object]= refs;

//为具体的key赋值

(*refs)[key] =ObjcAssociation(policy, new_value);

//设置类中的标志

Class cls =object->getIsa();

cls->setInstancesHaveAssociatedObjects();

}

} else {//新值为空,可用于删除某个关联对象

// setting the association to nil breaks the association.

AssociationsHashMap::iterator i =associations.find(disguised_object);

//如果有绑定到该对象的关联引用

if (i != associations.end()) {

//取得绑定的关联对象引用表

ObjectAssociationMap *refs =i->second;

//查找key对应的关联对象

ObjectAssociationMap::iteratorj = refs->find(key);

if (j != refs->end()) {//如果已经绑定了关联引用

//保存旧值,后面还要进一步处理

old_association =j->second;

//从map中删掉该项

refs->erase(j);

}

}

}

}

// release the old value (outside of the lock).

//根据其原本绑定的策略,对删掉的值做一些善后处理

if (old_association.hasValue())ReleaseValue()(old_association);

}

第二个(objc_getAssociatedObject)实现:

id_object_get_associative_reference(id object, void *key) {

id value = nil;

//将policy的初值赋为0

uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;

{

AssociationsManager manager;

//获得全局map对象的引用

AssociationsHashMap&associations(manager.associations());

//对object做DISGUISE操作

disguised_ptr_t disguised_object =DISGUISE(object);

//以object的DISGUISE操作结果作为key在HashMap中查找相应的值

AssociationsHashMap::iterator i =associations.find(disguised_object);

//有对应的结果执行下列语句;没有则返回nil;

if (i != associations.end()) {

//取得上次查找结果的second部分(该部分还是一个map)

ObjectAssociationMap *refs =i->second;

//根据传入的key进一步在上次的结果集中查找

ObjectAssociationMap::iterator j =refs->find(key);

//如果有对应的值,进行下一步;否则返回nil;

if (j != refs->end()) {

//取出关联对象的模型实体(包括了关联的策略和关联的具体对象)

ObjcAssociation &entry =j->second;

//取得关联的对象

value = entry.value();

//取得关联的策略

policy = entry.policy();

//根据相应的policy做一些后续操作

if (policy & OBJC_ASSOCIATION_GETTER_RETAIN)((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);

}

}

}

//有值的时候进行policy的检测和处理

if (value && (policy &OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {

((id(*)(id, SEL))objc_msgSend)(value,SEL_autorelease);

}

return value;

}

第三个(objc_removeAssociatedObjects)如下:

//清除所有关联的对象

void_object_remove_assocations(id object) {

vector<ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;

{

AssociationsManager manager;

AssociationsHashMap&associations(manager.associations());

//整个程序还没有实例关联任何对象

if (associations.size() == 0) return;

disguised_ptr_t disguised_object =DISGUISE(object);

AssociationsHashMap::iterator i =associations.find(disguised_object);

//该对象有关联引用要处理

if (i != associations.end()) {

// copy all of the associations that need to be removed.

//找到它所关联的所有对象(一个map)

ObjectAssociationMap *refs =i->second;

//将所有要删除的对象保存起来

for (ObjectAssociationMap::iteratorj = refs->begin(), end = refs->end(); j != end; ++j) {

elements.push_back(j->second);

}

// remove the secondary table.

delete refs;

//在全局关联引用map中,删除该对象对应的表

associations.erase(i);

}

}

// the calls toreleaseValue() happen outside of the lock.

//删除指定对象所有的关联值,会根据设置的policy做一些后续的操作,详情见上面介绍的函数。

for_each(elements.begin(), elements.end(),ReleaseValue());

}

通过上面的分析我们也可以知道,其实关联引用就是一个二级map的建立、查找、删除的操作。假设已建好了所有需要的表结构,其搜索过程如下图所示:



综上所述我们可以得出如下结论:关联引用的关联是以实例对象为基本单位进行的,即使是同一个类的实例对象,它们所关联的对象个数和值都可以不同;已关联的对象可以选择性删除或全部删除;关联的对象只要设置合适的策略就可以在撤销关联时不用手动置为nil,相关函数会帮我们完成这项操作(*^-^*)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: