Association 关联对象
默认情况下,由于分类底层结构的限制,不能直接给 Category 添加成员变量,但是可以通过关联对象间接实现 Category 有成员变量的效果。
使用方法
1 |
|
objc_AssociationPolicy 关联策略
1 | typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { |
key的常见用法
1 | // 使用 getter 方法的 SEL 作为 key(可读性高,有智能提示) |
关联对象的原理
实现关联对象技术的核心对象
AssociationsManager
AssociationsHashMap
ObjectAssociationMap
ObjcAssociation
1 | class AssociationsManager { |
数据结构
- AssociationsManager
关联对象并不是存储在关联对象本身内存中,而是存储在全局统一的一个容器中;
由 AssociationsManager 管理并在它维护的一个单例 Hash 表 AssociationsHashMap 中存储;
使用 AssociationsManagerLock 自旋锁保证了线程安全。
AssociationsHashMap
一个单例的 Hash 表,存储 disguised_ptr_t 和 ObjectAssociationMap 之间的映射。
disguised_ptr_t 是根据 object 生成,但不存在引用关系。
disguised_ptr_t disguised_object = DISGUISE(object);
- ObjectAssociationMap
存储 key 和 ObjcAssociation 之间的映射
ObjcAssociation
存储着关联策略 policy 和关联对象的值 valueobjc_setAssociatedObject
① 实例化一个 AssociationsManager 对象,它维护了一个单例 Hash 表 AssociationsHashMap 对象;
② 实例化一个 AssociationsHashMap 对象,它维护 disguised_ptr_t 和 ObjectAssociationMap 对象之间的关系;
③ 根据object生成一个 disguised_ptr_t 对象;
④ 根据 disguised_ptr_t 获取对应的 ObjectAssociationMap 对象,它存储key和 ObjcAssociation 之间的映射;
⑤ 根据policy和value创建一个 ObjcAssociation 对象,并存储在 ObjectAssociationMap 中;
⑥ 如果传进来的value为 nil,则在 ObjectAssociationMap 中删除该 ObjcAssociation 对象。
- objc_getAssociatedObject
① 实例化一个 AssociationsManager 对象;
② 实例化一个 AssociationsHashMap 对象;
③ 根据object生成一个 disguised_ptr_t 对象;
④ 根据 disguised_ptr_t 获取对应的 ObjectAssociationMap 对象;
⑤ 根据 key 获取到它所对应的 ObjcAssociation 对象;
⑥ 返回 ObjcAssociation 中的 value。
- objc_removeAssociatedObjects
① 实例化一个 AssociationsManager 对象;
② 实例化一个 AssociationsHashMap 对象;
③ 根据object生成一个 disguised_ptr_t 对象;
④ 根据 disguised_ptr_t 获取对应的 ObjectAssociationMap 对象;
⑤ 删除该 ObjectAssociationMap 对象。
- acquireValue
根据policy来对value进行retain或者copy操作。
相关面试题
Q:如何移除关联对象?
移除一个object的某个key的关联对象:调用
objc_setAssociatedObject
设置关联对象value为nil。
objc_setAssociatedObject函数会调用_object_set_associative_reference函数,并在该函数中判断传进来的value是否为nil,是的话会调用erase(j)擦除函数,将j变量擦除。j即为ObjectAssociationMap对象里的一对【key: key value: ObjcAssociation(_policy、_value)】。移除一个object的所有关联对象:调用函数
objc_removeAssociatedObjects
。
objc_removeAssociatedObjects函数会调用_object_remove_assocations
函数,并在该函数中调用对象的erase(i)擦除函数,将i变量擦除。i即为AssociationsHashMap对象中的一对【key: object value: ObjectAssociationMap】。
Q:如果 object 被销毁,那它所对应的 ObjectAssociationMap 是否也会自动销毁?
会。
如果没有关联对象,怎么实现 Category 有成员变量的效果?
使用字典。创建一个全局的字典,将self对象在内存中的地址作为key。
缺点:
① 内存泄漏问题:全局变量会一直存储在内存中;
② 线程安全问题:可能会有多个对象同时访问字典,加锁可以解决;
③ 每添加一个成员变量就要创建一个字典,很麻烦。
1 |
|