我们知道关联引用(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,相关函数会帮我们完成这项操作(*^-^*)