iOS关联对象技术原理
iOS 通过 runtime 的 API 能给分类增加属性,关联属性总共有下边3个 API
///获取某个对象的关联属性
idobjc_getAssociatedObject(idobject,constvoid*key){
return_object_get_associative_reference(object,?(void*)key);
}
///给某个对象增加关联属性
voidobjc_setAssociatedObject(idobject,constvoid*key,?idvalue,?objc_AssociationPolicy?policy){
_object_set_associative_reference(object,?(void*)key,value,?policy);
}
///移除对象所有的关联属性
voidobjc_removeAssociatedObjects(idobject)
通过 runtime 的源码能看出关联属性并没有增加到 category_t(分类)里边,运行时也不会合并到元类对象里边,而是存储在一个全局的AssociationsManager 里边,下边是这个 AssociationsManager 包含的层级关系.
所有的关联属性 和 获取关联属性 移除关联属性都是通过一个 AssociationsManager来操作,相似于 OC 中 NSFileManager 的角色,通过传递进来的对象作为地址 取出这个对象所对应的关联列表,而后通过key 取出这个关联列表的关联属性 ObjcAssociation, ObjcAssociation 包含了关联策略 和 关联值.
下边我会通过解读源码 来分析AssociationsManager是如何要关联的值和对象建立联络的.
AssociationsManager 是一个 C++的类 使用来进行对关联对象的属性增加 和 查找 移除等操作
classAssociationsManager{
staticspinlock_t_lock;
staticAssociationsHashMap?*_map;//?associative?references:??object?pointer?->?PtrPtrHashMap.?这个_?map?里边存储的有关联列表
public:
AssociationsManager()???{?_lock.lock();?}
~AssociationsManager()??{?_lock.unlock();?}
AssociationsHashMap?&associations(){//能看成是只初始化一次?相似与单例
if(_map?==NULL)
_map?=newAssociationsHashMap();
return*_map;
}
};
关联列表是一个 hashMap 相似于 OC 的 NSDictionary ,其中使用 disguised_ptr_t 作为 key , ?ObjectAssociationMap * 作为一个 value
disguised_ptr_t 是 ?uintptr_t 的类型
intptr_t 和uintptr_t 类型使用来存放指针地址。它们提供了一种可移植且安全的方法公告指针,而且和系统中用的指针长度相同,对于把指针转化成整数形式来说很有使用。
能把disguised_ptr_t了解为一个指针类型的变量
classAssociationsHashMap:publicunordered_map?{
public:
void*operatornew(size_tn){return::malloc(n);?}
voidoperatordelete(void*ptr){?::free(ptr);?}
};
ObjectAssociationMap 也是一个 HashMap 存放的是 一个 void * key 就是关联属性时传进来的 key , ?ObjcAssociation 存放的关联属性策略和值的信息
classObjectAssociationMap:publicstd::map?{
public:
void*operatornew(size_tn){return::malloc(n);?}
voidoperatordelete(void*ptr){?::free(ptr);?}
};
ObjcAssociation 关联属性信息类 存放了关联策略 和 传递进来关联的值 id 类型
classObjcAssociation{
uintptr_t_policy;
id?_value;
public:
ObjcAssociation(uintptr_tpolicy,?id?value)?:?_policy(policy),?_value(value)?{}
ObjcAssociation()?:?_policy(0),?_value(nil)?{}
uintptr_tpolicy()const{return_policy;?}
idvalue()const{return_value;?}
boolhasValue(){return_value?!=?nil;?}
};
下边是对 objc_getAssociatedObject , objc_setAssociatedObject , objc_removeAssociatedObjects 具体实现的分析
objc_setAssociatedObject 增加关联属性的 API
void?_object_set_associative_reference(id?object,?void?*key,?id?value,?uintptr_t?policy)?{
//?retain?the?new?value?(if?any)?outside?the?lock.
///?旧的关联对象?由于关联属性时假如传?nil?可可以会替换旧的关联属性?,这就是移除某个关联属性时传?nil?的起因
ObjcAssociation?old_association(0,?nil);
id?new_value?=?value???acquireValue(value,?policy)?:?nil;
{
AssociationsManager?manager;
///获取关联属性列表?,取出来的列表是以对象为单位的?,即某个对象的关联列表?,这样即可以单独的关联某个对象的关联属性?而不与其余对象隔离开
AssociationsHashMap?&associations(manager.associations());
///?将要增加关联属性的对象产生一个内存地址?做?key?存储?它的关联属性
disguised_ptr_t?disguised_object?=?DISGUISE(object);
///?假如要关联的值不为空?,不为空时?就需要判断这个属性和?key?是不是第一天增加?,即??void?*key,?id?value?都是第一次传递进来?
if(new_value)?{
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;//取出旧值?后边对这个旧值进行?release?操作
///将新值存放到?key?对应的字典中去?
j->second?=?ObjcAssociation(policy,?new_value);
}else{///没有旧值直接将新值增加到字典里
(*refs)[key]?=?ObjcAssociation(policy,?new_value);
}
}else{
假如?key?对象的字典不存在?就创立一个字典?(hashMap?相似于字典的功可以,本文为了方便了解将它称为字典)
ObjectAssociationMap?*refs?=newObjectAssociationMap;
associations[disguised_object]?=?refs;
///将要关联属性和策略封装到一个ObjcAssociation类里边?并根据?key?增加到这个字典里
(*refs)[key]?=?ObjcAssociation(policy,?new_value);
object->setHasAssociatedObjects();
}
}else{
///假如增加关联的属性为空时?就需要取出之前关联的值?并把它擦除掉?相当于removeObjectForKey?
///还是根据对象内存地址找到它的关联属性列表?,而后通过?key?找到它关联属性的实体(ObjcAssociation这个类)?最后擦除掉?相当于?free?从内存中移除
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);
}
objc_getAssociatedObject 关联对象取值的操作
id_object_get_associative_reference(idobject,void*key)?{
idvalue?=nil;
uintptr_t?policy?=?OBJC_ASSOCIATION_ASSIGN;
{
///还是通过?AssociationsManager?找到所有关联对象类别?,而后通过传入?object?找到某个对象的关联列表?,而后通过?key?找到这个对象关联属性列表的某个实体(ObjcAssociation)?最后根据关联策略返回这个属性的值?
AssociationsManager?manager;
AssociationsHashMap?&associations(manager.associations());
disguised_ptr_t?disguised_object?=?DISGUISE(object);
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())?{///假如对象关联列表的属性存在
ObjcAssociation?&entry?=?j->second;
value?=?entry.value();
policy?=?entry.policy();
///取出关联值和策略?发送消息?相似与?[obj?retain]
if(policy?&?OBJC_ASSOCIATION_GETTER_RETAIN)?((id(*)(id,?SEL))objc_msgSend)(value,?SEL_retain);
}
}
}
///?假如这个对象是延时释放的类型?相似与?OC?Array?String?这些不是?alloc?来的对象?都要执行?[obj?autorelease]来释放?
if(value?&&?(policy?&?OBJC_ASSOCIATION_GETTER_AUTORELEASE))?{
((id(*)(id,?SEL))objc_msgSend)(value,?SEL_autorelease);
}
returnvalue;
}
objc_removeAssociatedObjects 移除该对象所有的关联属性列表
void?_object_remove_assocations(id?object)?{
vector<?ObjcAssociation,ObjcAllocator?>?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);
///假如这个对象有关联的属性列表?那么久便利它关联的属性列表?而后通过便利将这些关联内容?一个个从字典里边擦除??先擦除对象列表关联的属性列表?而后将这个对象关联属性的?hashMap?擦除掉?相当于?[dict?removeAllObjects]?而后再从全局?AssociationsManager?移除?这个对象关联的字典?,?又相当于?从一个全局大字典里?把?dict这个对象的小字典?给移除了?
if(i?!=?associations.end())?{
//?copy?all?of?the?associations?that?need?to?be?removed.
ObjectAssociationMap?*refs?=?i->second;
for(ObjectAssociationMap::iterator?j?=?refs->begin(),?end?=?refs->end();?j?!=?end;?++j)?{
elements.push_back(j->second);
}
//?remove?the?secondary?table.
delete?refs;
associations.erase(i);
}
}
//?the?calls?to?releaseValue()?happen?outside?of?the?lock.
for_each(elements.begin(),?elements.end(),?ReleaseValue());
}
以上代码看起来并不难 除了少量 C++ 语法难以了解外 也并不需要完全知道每行代码怎样实现 ,大概思路就是 通过全局大字典 ,找到某个对象相关的小字典 ,而后这个小字典里存放了 一个key 对应一个属性值,最后取出这个管理属性的策略和值
写到最后 AssociationsManager 这个类不是一个单例类
class AssociationsManager {
static spinlock_t _lock;
static AssociationsHashMap *_map; ? ? ? ? ? ? ? // associative references: ?object pointer -> PtrPtrHashMap.
public:
AssociationsManager() ? { _lock.lock(); } //默认无参构造函数 对象创立时自动调使用 执行加锁 这样多个线程访问 _map 时不会出现问题
~AssociationsManager() ?{ _lock.unlock(); } //析构函数 对象能否时候进行解锁的操作
///能看做单例对象 在 ?AssociationsManager 创立时候 加锁 当 AssociationsManager 释放时候 解锁 ,防止多线程访问时候 对同一个 ?_map 屡次创立 是一种懒汉模式单例
AssociationsHashMap &associations() {
if (_map == NULL)
_map = new AssociationsHashMap();
return *_map;
}
};
它里边有个 spinlock_t锁 对 _map 这个全局唯一的实例 进行加锁和解锁 ,因为懒汉模式的单例 需要在多个线程访问 ?_map 时候进行加锁保护
其实做为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS学习交流群 651612063,不论你是小白还是大牛欢迎入驻,大家一起交流学习,加群私聊(小狄)即可以领取2018最全梳理的面试宝典和资料)。同时想要找工作的也能私聊小编。
需要资料的也能加小编QQ2507362121。
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » iOS关联对象技术原理