runtime关联对象的基本使用
runtime关联对象有什么用途呢?我们可以利用关联对象来给Category(分类)间接地添加成员变量而且关联对象不会影响到原来类对象的内存结构。
objc/runtime.h中提供的关联对象的API有以下3个:
- (1)添加关联对象
void objc_setAssociatedObject(id object, const void * key,id value, objc_AssociationPolicy policy)
- (2)获取关联对象
id objc_getAssociatedObject(id object, const void * key)
- (3)移除所有的关联对象
void objc_removeAssociatedObjects(id object)
其中的参数key的常见用法有以下几种:
1 | //用法1: |
1 | //用法2: |
1 | //用法3:使用属性名作为key |
1 | //用法4:使用get方法的@selecor作为key,此方法可读性更高,推荐使用方法4 |
其中的参数objc_AssociationPolicy(关联策略)的枚举值有以下几个:
- OBJC_ASSOCIATION_ASSIGN 其对应的修饰符是assign
- OBJC_ASSOCIATION_RETAIN_NONATOMIC 其对应的修饰符是strong,nonatomic
- OBJC_ASSOCIATION_COPY_NONATOMIC 对应的修饰符是copy,nonatomic
- OBJC_ASSOCIATION_RETAIN 对应的修饰符是strong,atomic
- OBJC_ASSOCIATION_COPY 其对应的修饰符是copy,atomic
添加关联对象objc_setAssociatedObject的底层实现原理
Apple源码地址:https://opensource.apple.com/tarballs/objc4/
objc4-750的objc-references.mm中objc_setAssociatedObject方法源码如下:
(1) objc_setAssociatedObject的底层实现:objc_setAssociatedObject中直接调用了_object_set_associative_reference(object, (void *)key, value, policy)
1 | void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) { |
(2) _object_set_associative_reference的底层实现
1 | void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) { |
(3)其中的AssociationsManager的内部实现
1 | class AssociationsManager { |
(4) 再看AssociationsHashMap的底层实现
1 | class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> { |
(5) ObjectAssociationMap的底层实现
1 | class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> { |
(6) ObjcAssociation的内部实现
1 | class ObjcAssociation { |
实现关联对象技术的核心对象有:
- AssociationsManager
- AssociationHashMap
- ObjectAssociationMap
- ObjcAssociation
以上源码可以简化为以下逻辑关系图:
关联对象并不是存储在被关联对象本身内存中,而是存储在全局的统一的AssociationsManager中。如果设置某个关联对象的value为nil,相当于移除该关联对象。
获取关联对象objc_getAssociatedObject的底层实现原理
objc4-750的objc-references.mm中objc_getAssociatedObject方法源码如下:
(1)objc_getAssociatedObject的内部实现
1 | id objc_getAssociatedObject(id object, const void *key) { |
(2)_object_get_associative_reference的底层实现
1 | id _object_get_associative_reference(id object, void *key) { |
首先通过传进来的参数object计算得到disguised_object,再将disguised_object作为associations.find方法的参数传入并得到i,再通过i->second,得到ObjectAssociationMap *refs对象;然后refs通过参数key计算得到j,利用j->second,得到ObjcAssociation &entry;又通过entry.value()方法和entry.policy()方法分别计算得到value和policy(关联策略),最后返回value值。
移除所有关联对象objc_removeAssociatedObjects的内部实现
objc4-750的objc-references.mm中objc_removeAssociatedObjects方法源码如下:
1 | void objc_removeAssociatedObjects(id object) |
_object_remove_assocations(object)的内部实现如下:
1 | void _object_remove_assocations(id object) { |
首先调用_object_remove_assocations函数,在_object_remove_assocations内部先通过参数object计算得到disguised_object,再通过disguised_object计算获得i,然后通过i->second获得ObjectAssociationMap *refs,接下来遍历ref获得j,移除j->second(关联对象)也就是把关联对象的hashMap清空,最后将对象的这个hasMap从全局的AssociationsHashMap中移除。