type
status
date
slug
summary
tags
category
icon
password
本文是Objective-C系列的第6篇,主要讲述了关联对象的底层结构和使用。
一、为什么需要关联对象?
1. Category能添加成员变量吗?
在Objective-C(四)Category中讲过,Category能添加协议、方法、属性等,参考下面的结构体。
既然可以添加属性,那是不是可以添加成员变量呢?
答案是:不可以。
如上面的实例,调用后,Crash
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[BFPerson setAge:]: unrecognized selector sent to instance 0x60000202c4b0'
所以,我们得知:
Category可以添加属性,但不会添加对应的成员变量,也不会实现对应的setter、getter方法。
2. 如何实现给Category添加成员变量?
实例源码参考01-Category成员变量。
既然无法直接给分类中添加,我们可以通过全局变量的方式来保存成员变量的值,且实现对应的setter、getter来模拟为Category完整添加属性(成员变量)的效果。
上述方案有一些缺点:
- 每添加一个属性,就需要创建一个字典来保存对应成员变量
- 抑或共用一个字典,但必须要保证不同属性对应不同的key;
- 实现繁琐。
3. 什么是关联对象?
关联是指把两个对象相互关联起来,使得其中的一个对象作为另外一个对象的一部分。
在Objective-C中,关联对象(Associated Objects)允许您向现有类添加额外属性,而不需要继承自该类。这对于为现有类添加自定义属性或行为非常有用,尤其是当您无法修改该类的源代码时。
在Objective-C中,关联对象的实现是通过Runtime来完成的。Runtime提供了一组C函数来动态地操作Objective-C对象,其中就包括了关联对象的功能。底层实现主要依赖于
objc_setAssociatedObject
和objc_getAssociatedObject
函数。关联是基于关键字的,因此,我们可以为任何对象增加任意多的关联,每个都使用不同的关键字即可。关联是可以保证被关联的对象在关联对象的整个生命周期都是可用的(在垃圾自动回收环境下也不会导致资源不可回收)。
二、关联对象的使用
示例代码02-关联对象
1. 关联对象API
2. key
- 关键字是一个void类型的指针,必须唯一
- 常用静态变量来作为key。通常推荐key使用static char类型——使用指针或许更好,并只在getter和setter方法内部使用。
- 更简单的方案是:直接使用选择器(selector),因为SEL生成的时候就是一个唯一的常量。
3. policy
policy
表明了value
内存语义,是通过赋值,保留引用还是复制的方式进行关联的;
policy
还表明原子的还是非原子的。
这里的关联策略和声明属性时的很类似。这种关联策略是通过使用预先定义好的常量来表示的。
4. 应用
在本文开始就提出,关联对象作为给Category添加属性是一种高效可行的方案
但是,我们仍然需要提醒你:
- 关联对象应该被当做最后的手段来使用(不得不用时才用),而不是为了寻求一个解决方案就行。
category
本身就不应该是解决问题优先选择的工具
下面给出了一些其他开源库中用到的场景:
4.1 类增加状态
添加私有变量来帮助实现细节 。当拓展一个内置类时,可能有必要跟踪一些额外的状态,这是关联对象最普遍的应用场景。
例如:AFNetworking中在UIImageView的分类中使用关联对象来存储一个请求操作对象(operation object),用于异步的从远程获取图片。
4.2 解耦
使用关联对象来代替X,其中X代表下面的一些项:
- 子类化,当使用继承比使用组合更合适的时候。
- target-action给响应者添加交互事件。
- 按钮
- 手势识别,当target-action模式不够用的时候。
- 代理,当事件可以委托给其他对象。
- 消息 & 消息中心使用低耦合的方式来广播消息。
三、原理
1.核心对象
对应的API为:
1.1 AssociationsManage
全局关联对象管理类,其拥有一个
map字典
用于存放所有被关联对象object的关联对象。1.2 AssociationsHashMap
AssociationsHashMap
就是存放一个被关联对象object的所有管理对象的字典,根据下面的类定义:- 继承于
unordered_map
,后面三个参数,分别是hash
、equal
及allocator
函数的实现。
其中该字典对应的:
- key:
disguised_ptr_t
类型
- value:
ObjectAssociationMap
类型
1.3 ObjectAssociationMap
ObjectAssociationMap
是真正存储object中关联对象的载体。其中该字典:
- key:
void *
指针类型,即传进来的key。
- value:
ObjcAssociation
类型,该类型存储了传进来的value
和policy
。
1.4 ObjcAssociation
ObjcAssociation
用于存放关联对象的value
及policy
。2. 关联对象的结构
3.核心流程
3.1 设置关联对象
object
不能为nil
value
可为nil,nil表示清除该关联对象
3.2 获取关联对象
3.3 移除关联对象
参考
链接
- 示例代码01-Category成员变量
- 示例代码02-关联对象