美文网首页iOS面试题
如何给 Category 添加属性?关联对象以什么形式进行存储?

如何给 Category 添加属性?关联对象以什么形式进行存储?

作者: EmulatingStep | 来源:发表于2020-03-11 10:03 被阅读0次

    分类无法添加成员变量,在分类中定义了属性,系统没有生成对应的成员变量,也没有实现set和get方法。那我们如何实现为分类添加属性呢?

    通过runtime中提供的关联对象相关API我们可以实现以上功能。关联对象以哈希表的格式,存储在一个全局的单例中。

    // 添加关联对象
    void objc_setAssociatedObject(id object, const void * key,
                                    id value, objc_AssociationPolicy policy)
    
    // 获得关联对象
    id objc_getAssociatedObject(id object, const void * key)
    
    // 移除所有的关联对象
    void objc_removeAssociatedObjects(id object)
    

    关联API对象参数说明:

    • 参数一:id object:给哪个对象添加属性。
    • 参数二:const void * key:关联对象中属性值存取过程中对应唯一标识,根据key来设置和取值。
    • 参数三:id value:关联的值,也就是set方法传入的值给属性去保存。
    • 参数四:objc_AssociationPolicy policy:策略,属性以什么形式保存。
    typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
        OBJC_ASSOCIATION_ASSIGN = 0,  // 指定一个弱引用相关联的对象
        OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // 指定相关对象的强引用,非原子性
        OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  // 指定相关的对象被复制,非原子性
        OBJC_ASSOCIATION_RETAIN = 01401,  // 指定相关对象的强引用,原子性
        OBJC_ASSOCIATION_COPY = 01403     // 指定相关的对象被复制,原子性   
    };
    
    @interface NSObject (Extension)
    
    @property (nonatomic,copy  ) NSString *name;
    
    @end
    
    
    @implementation NSObject (Extension)
    
    - (void)setName:(NSString *)name {
        
        objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    
    - (NSString *)name {
        
        return objc_getAssociatedObject(self,@selector(name));
    }
    
    @end
    

    关联对象原理

    实现关联对象的核心对象有:

    1. AssociationsManager
    2. AssociationsHashMap
    3. ObjectAssociationMap
    4. ObjcAssociation

    其中Map就相当于我们平时使用的字典,也是key-value存取值。

    我们通过runtime源码来分析底层原理,来到objc-references.mm,搜索objc_setAssociatedObjectobjc_getAssociatedObjectobjc_removeAssociatedObjects,分别来看看设置,取值,移除所有关联对象的底层逻辑。

    /**********************************************************************
    * Associative Reference Support
    **********************************************************************/
    
    id objc_getAssociatedObject(id object, const void *key) {
        return _object_get_associative_reference(object, (void *)key);
    }
    
    
    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
        _object_set_associative_reference(object, (void *)key, value, policy);
    }
    
    
    void objc_removeAssociatedObjects(id object) 
    {
        if (object && object->hasAssociatedObjects()) {
            _object_remove_assocations(object);
        }
    }
    

    _object_set_associative_reference函数:

    _object_set_associative_reference函数源码

    接下来查看AssociationManagerAssociationsHashMapObjectAssociationMapObjcAssociation之间的关系。

    AssociationManager
    AssociationManager源码
    AssociationsHashMap、ObjectAssociationMap、ObjcAssociation
    AssociationsHashMap和ObjectAssociationMap源码

    通过AssociationsHashMap内部源码我们发现AssociationsHashMap继承自unordered_map首先来看一下unordered_map内的源码:

    unordered_map源码

    unordered_map源码中可以看出前两个参数_Key_Tp对应的就是map中的keyvalue,参照传入参数,_Key对应的值就是disguised_ptr_t_Tp对应的值就是ObjectAssociationMap *

    接着我们查看ObjectAssociationMap,继承至map,和unordered_map类似,同样以keyvalue方式存储着ObjcAssociation_Key对应着void *_Tp对应着ObjcAssociation

    再接着来查看ObjcAssociation

    ObjcAssociation源码

    我们发现ObjcAssociation存储着_policy_value,而这两个值我们可以发现正是我们调用objc_setAssociatedObject函数传入的值,也就是说我们在调用objc_setAssociatedObject函数中传入的valuepolicy这两个值最终是存储在ObjcAssociation中的。

    现在我们已经对AssociationsManagerAssociationsHashMapObjectAssociationMapObjcAssociation四个对象之间的关系有了简单的认识,那么接下来我们来细读源码。

    _object_set_associative_reference源码解读

    最后通过一张图整理其中关系,这样就看的更加清晰明了。

    关联对象原理图
    _object_get_associative_reference函数
    _object_get_associative_reference解读
    _object_remove_assocations函数
    _object_remove_assocations解读

    总结

    • 关联对象并不是存储在关联对象本身内存中
    • 关联对象存储在全局的一个AssociationsManager
    • 设置关联对象为nil,就相当于移除了关联对象

    iOS面试题汇总

    相关文章

      网友评论

        本文标题:如何给 Category 添加属性?关联对象以什么形式进行存储?

        本文链接:https://www.haomeiwen.com/subject/sgyfjhtx.html