iOS关联对象技术原理

作者 : 开心源码 本文共7089个字,预计阅读时间需要18分钟 发布时间: 2022-05-11 共157人阅读

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关联对象技术原理

发表回复