iOS底层系列:关联对象

前言

之前我们聊过了,在Category中声明一个属性,可以自己手动实现set和get方法,但是因为没有成员变量,所以说并不能储值。

我们可以通过runtime的api实现让成员变量可以储值,其实本质也并不是储存,而是通过关联对象实现了这种看似是可以储值的效果。

我们可以用下面的方法来设置关联对象。

objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)

objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key);

今天我们就来探究一下这种关联对象的实现逻辑。

实现

我们在runtime源码中搜索objc_setAssociatedObject,最终可以定位到下面这个方法。

_object_set_associative_reference

通过简单的分析源码,我们可以看出关联对象的实现,大致是由下面四个类结合实现的。

AssociationsManager
AssociationsHashMap
ObjectAssociationMap
ObjcAssociation

简单的抽取和简化一下源码,基本可以得出这四个类的关系。

class AssociationsManager {
    static AssociationsHashMap * _map
}

typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;

typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;

class ObjcAssociation {
    uintptr_t _policy;
    id _value;
};

大家也可以看一下下面这幅图。

association_1.png

这里很清晰的表明了这几个类的关系,AssociationsManager有一个AssociationsHashMap类型的属性_map,_map的key是DisguisedPtr<objc_object>类型,value是ObjectAssociationMap类型,而这个ObjectAssociationMap类型中的key是一个指针(void*),value是ObjcAssociation类型。这个ObjcAssociation中有两个重要的是就是属性,_policy和_value。

关联对象的设置就是通过这几个类来实现的,上面我们也分析完了这几个类的相互关系,那这些类和我们在调用objc_setAssociatedObject时传入的参数关系是怎么样的呢?我们继续分析源码。

我们在看一遍下面这方法。

objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)

在调用的时候我们传入了四个参数,第一个就是我们的被关联对象,第二个是我们设置的关联对象的key, 第三个就是我们要存的值,第四个就是关联的策略(类似retain,copy等)

这里先直接说结果吧,其实可以理解为AssociationsManager的属性_map中,以参数object为key,value是一个ObjectAssociationMap类型的map。ObjectAssociationMap中,则是以参数中的key为key,value是一个ObjcAssociation类型的对象,最后我们参数中的value和policy就储存在这个ObjectAssociation变量中。

好了,这样我们传的四个参就跟这些类对应上了。就像下图这样子。

association_2.png

注意:

通过源码我们知道,AssociationsHashMap中的key并不是使用直接使用了object,而是一个DisguisedPtr类型,但是通过源码我们可以看到DisguisedPtr<objc_object> disguised{(objc_object *)object};,说到底这个key也是根据我们的object来生成的,所以可以说这个key与我们的object是对应关系,从而可以理解为是这个object为key。

不得不说,苹果的设计还是很巧妙的,每一个要设置关联对象的对象对应一个map,在这个map中,使用我们自己设置的不同的关联对象的key为key,用关联对象的value和策略生成一个ObjcAssociation类型的对象为value,然后进行储存,这样每一个要设置关联对象的对象,具体的每一个关联对象,都能一一对应起来了。

上面的话说的有点拗口,相信大家可以理解。

总结和补充

关联对象并不是储存在被关联对象本身的,而是储存在全局的统一的一个AssociationsHashMap中。

从源码中我们还可以看出如果我们给关联对象设置nil,则代表移除该关联对象。

同时还要注意,因为我们的object其实是一个对象,这就要涉及内存管理问题,当我们的这个object释放后,其实整个AssociationsHashMap中其对应项都会被移除,这个以后我们讨论内存管理的时候再说。

objc_getAssociatedObject实现咱们就不具体分析了,如果看动了set的实现,get其实很容易理解。

感谢阅读。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335