Associated Object 源码解析
2020/3/30 23:02:32
本文主要是介绍Associated Object 源码解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Associated Object 源码解析
我们都知道,在NSObject对象中,我们可以创建一个category,来对object做一些辅助性质的工作,比如代码的结偶啊等等,可以动态的为对象添加一些新的行为。那么他们是怎么实现的呢,那么我们就看看runtime的源码中,关于associated object是如何实现的吧。
首先我们可以创建一个NSObject的Category,然后动态的添加一个associatedObject的属性:
// NSObject+Associated.h @interface NSObject (Associated) @property (nonatomic, strong) id associatedObject; @end // NSObject+Associated.m @implementation NSObject (Associated) @dynamic associatedObject; + (void)load { NSLog(@"bbb..."); } - (void)setAssociatedObject:(id)object { objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (id)associatedObject { return objc_getAssociatedObject(self, @selector(associatedObject)); } 复制代码
然后我么就可以给associatedObject赋值了:
NSObject *object = [NSObject new]; object.associatedObject = [NSObject object]; 复制代码
这样我们就完成了一个常见的associated property的调用。
源码分析
objc_setAssociatedObject分析
我们看一下,objc_setAssociatedObject都经历了什么吧。
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) { _object_set_associative_reference(object, (void *)key, value, policy); } void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) { // retain the new value (if any) outside the lock. ObjcAssociation old_association(0, nil); /** // 创建一个符合policy规则的值, 符合的值为: OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1 OBJC_ASSOCIATION_COPY_NONATOMIC = 3 剩下的值都为assign */ id new_value = value ? acquireValue(value, policy) : nil; { // 创建AssociationsManager对象 AssociationsManager manager; // 初始化manager中的_map AssociationsHashMap &associations(manager.associations()); // 对object的地址按位取反, 作为_map的key disguised_ptr_t disguised_object = DISGUISE(object); if (new_value) { // break any existing association. AssociationsHashMap::iterator i = associations.find(disguised_object); if (i != associations.end()) { // 从_map中找,associtates是否有key值为disguised_object的对象,即ObjectAssociationMap ObjectAssociationMap *refs = i->second; // 从ObjectAssociationMap中找key对应的ObjcAssociation,如果有的话,则替换,没有就新建 ObjectAssociationMap::iterator j = refs->find(key); if (j != refs->end()) { old_association = j->second; j->second = ObjcAssociation(policy, new_value); } else { (*refs)[key] = ObjcAssociation(policy, new_value); } } else { // create the new association (first time). // 将这个类放入_map中,生成一个ObjectAssociationMap, 即_map[disguised_object] = refs ObjectAssociationMap *refs = new ObjectAssociationMap; associations[disguised_object] = refs; // 将key与value对应 (*refs)[key] = ObjcAssociation(policy, new_value); // 将has_assoc置为true object->setHasAssociatedObjects(); } } else { // setting the association to nil breaks the association. // 如果传入的object = nil,则擦除这个key对应的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; refs->erase(j); } } } } // release the old value (outside of the lock). // 释放old value if (old_association.hasValue()) ReleaseValue()(old_association); } 复制代码
从源码我们可以看到,void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy)
的流程如下:
-
首先获取判断value是否为空,如果不为空, 那么执行acquireValue方法,获取一个新的值
-
然后生成一个AssociationsManager,并生成一个associations,对associtaed property进行全局管理
-
然后判断newValue是否为空:
- 如果不为空,则去associations中查找,是否存在这个object对应的ObjectAssociationMap,
- 如果没有的话,则生成一个新的,并将它与object的DISGUISE(object)方法生成的key进行绑定,并将key与生成的ObjcAssociation(policy, new_value)对象进行绑定,然后将object中的has_assoc字段置为true,表示这个对象已经有了associated object。
- 如果已经存在的话,那么直接从_map中读取这个object对应的所有associated property,然后去查找,这个hashMap中是否存在对应的associated对象,如果存在,则先获取之前的对象,(因为最后要释放这个对象),然后赋予它一个新的值。否者直接赋值。
- 如果为空的话,那么则擦除这个key所对应的值,并获取之前的这个key所对应的value
- 如果不为空,则去associations中查找,是否存在这个object对应的ObjectAssociationMap,
-
最后,release之前的old value
从源码我们可以看到,objc_setAssociatedObject方法还是比较简单清晰的,就是创建一张全局的关联属性的表,一个对象,对应一个ObjectAssociationMap,然后将传入的key与value传递给他们,当然,需要进行一些判断,比如这个value的policy是什么,传入的值是否为nil等等,但是总体流程还是非常清晰的。
objc_getAssociatedObject
我们看这个方法的函数定义如下:
id objc_getAssociatedObject(id object, const void *key) 复制代码
是不是其实就是说,从一个hashMap中,找到key对应的value呢。有了上面的对于objc_setAssociatedObject的分析,我们直接看源码注释,应该理解上就没什么问题了:
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) { // retain the new value (if any) outside the lock. ObjcAssociation old_association(0, nil); /** // 创建一个符合policy规则的值, 符合的值为: OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1 OBJC_ASSOCIATION_COPY_NONATOMIC = 3 剩下的值都为assign */ id new_value = value ? acquireValue(value, policy) : nil; { // 创建AssociationsManager对象 AssociationsManager manager; // 初始化manager中的_map AssociationsHashMap &associations(manager.associations()); // 对object的地址按位取反 disguised_ptr_t disguised_object = DISGUISE(object); if (new_value) { // break any existing association. AssociationsHashMap::iterator i = associations.find(disguised_object); if (i != associations.end()) { // 从_map中找,associtates是否有key值为disguised_object的对象,即ObjectAssociationMap ObjectAssociationMap *refs = i->second; // 从ObjectAssociationMap中找key对应的ObjcAssociation,如果有的话,则替换,没有就新建 ObjectAssociationMap::iterator j = refs->find(key); if (j != refs->end()) { old_association = j->second; j->second = ObjcAssociation(policy, new_value); } else { (*refs)[key] = ObjcAssociation(policy, new_value); } } else { // create the new association (first time). // 将这个类放入_map中,生成一个ObjectAssociationMap, 即_map[disguised_object] = refs ObjectAssociationMap *refs = new ObjectAssociationMap; associations[disguised_object] = refs; // 将key与value对应 (*refs)[key] = ObjcAssociation(policy, new_value); // 将has_assoc置为true object->setHasAssociatedObjects(); } } else { // setting the association to nil breaks the association. // 如果传入的object = nil,则擦除这个key对应的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; refs->erase(j); } } } } // release the old value (outside of the lock). // 释放old value if (old_association.hasValue()) ReleaseValue()(old_association); } 复制代码
总结
在Mattt大神的博客:
中,他对关联属性的使用场景如下:
- Adding private variables to facilitate implementation details(添加一些私有的便利的方法实现)
- Adding public properties to configure category behavior.(添加一些共有的属性来配置这个分类)
- Creating an associated observer for KVO(在KVO中创建一个关联的观察者)
当然他也提到了,我们不应该在如下的场景中使用关联属性:
-
Storing an associated object, when the value is not needed(当这个值不再需要的时候,我们不该存储这个关联对象)
-
Storing an associated object, when the value can be inferred(当这个值可以被推断出来的时候,我们不该使用关联对象)
-
Using associated objects instead of X,如:
Subclassing for when inheritance is a more reasonable fit than composition.(当继承比组合更合理的情况使用关联对象)
这篇关于Associated Object 源码解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-10-05Swift语法学习--基于协议进行网络请求
- 2022-08-17Apple开发_Swift语言地标注释
- 2022-07-24Swift 初见
- 2022-05-22SwiftUI App 支持多语种 All In One
- 2022-05-10SwiftUI 组件参数简写 All In One
- 2022-04-14SwiftUI 学习笔记
- 2022-02-23Swift 文件夹和文件操作
- 2022-02-17Swift中使用KVO
- 2022-02-08Swift 汇编 String array
- 2022-01-30SwiftUI3.0页面反向传值