美文网首页
关联对象

关联对象

作者: 滨滨_57b5 | 来源:发表于2019-02-13 15:07 被阅读0次

    由于分类是不能直接添加成员变量的,因此需要使用关联对象技术为分类添加成员变量,由此引出几个问题:

    源码分析:objc_setAssociatedObject

    入口函数

    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
       
       _object_set_associative_reference(object, (void *)key, value, policy);//其中key做了一次强制类型转换
    }
    
    • ❗️ 参数key是一个任意类型的常量指针,可以传入@selector() 结构体指针SEL类型

    主函数

    void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    
        //1.创建一个空的ObjcAssociation对象
        ObjcAssociation old_association(0, nil);
    
        // 2.对关联对象进行内存管理操作retain/copy
        id new_value = value ? acquireValue(value, policy) : nil;
        {
            //3.创建AssociationsManager对象和其成员变量AssociationsHashMap对象
            AssociationsManager manager;
            AssociationsHashMap &associations(manager.associations());
            
            //4.将被关联对象的指针地址反转
            disguised_ptr_t disguised_object = DISGUISE(object);
    
            // 5.判断传入的关联对象是否为空
            if (new_value) {
                 //如果不为空
                 /*通过被关联对象的指针地址从AssociationsHashMap中获取对应的      
                  ObjectAssociationMap
                */
                AssociationsHashMap::iterator i = associations.find(disguised_object);
                //如果该被关联对象已经存在了对应的ObjectAssociationMap
                if (i != associations.end()) {
                    // 通过参数key获取到ObjectAssociationMap对应的ObjcAssociation
                    ObjectAssociationMap *refs = i->second;
                    ObjectAssociationMap::iterator j = refs->find(key);
                    //如果存在对应的ObjcAssociation
                    if (j != refs->end()) {
                        //把开始创建的空的ObjcAssociation对象设置为找到的这个ObjcAssociation
                        old_association = j->second;
                        //把当前的关联对象封装为ObjcAssociation并赋值给ObjectAssociationMap中key对应的值
                        j->second = ObjcAssociation(policy, new_value);
                    } else {//如果没有找到存在对应的ObjcAssociation,那就根据参数key直接设置
                        (*refs)[key] = ObjcAssociation(policy, new_value);
                    }
                } else { //如果该被关联对象不存在了对应的ObjectAssociationMap
                    // 那么就创建新的ObjectAssociationMap
                    ObjectAssociationMap *refs = new ObjectAssociationMap;
                    //以被关联对象的反转地址为key,新创建的ObjectAssociationMap为value,映射为ObjectAssociationHashMap
                    associations[disguised_object] = refs;
                    //把参数key作为ObjectAssociationMap的key,ObjcAssociation对象为value
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                    //最后设置这个被关联对象为有关联对象
                    object->setHasAssociatedObjects();
                }
            } else {//如果传入的关联对象为nil,会将被关联对象的对应关联对象擦除
                // setting the association to nil breaks the association.
                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);
                    }
                }
            }
        }
        // 最后将old_association擦除
        if (old_association.hasValue()) ReleaseValue()(old_association);
    }
    
    • ❗️ 这个函数就是主要的实现函数,关联对象是通过一个c++类AssociationsManager中的一个静态成员变量AssociationsHashMap (也是一个c++类)存储的
    • ❗️ 其中AssociationsHashMap是一个静态的,也就是全局的唯一的副本,是通过哈希查找实现的,即所有类的关联对象都放在AssociationsHashMap
    • ❗️ 首先会将参数value和policy进行关联,并封装成一个c++类ObjcAssociation,其中ObjcAssociation有俩个成员变量,value关联对象的地址和policy关联策略
    • ❗️ 通过参数key和封装好的ObjcAssociation映射成一个ObjectAssociationMapc++类,查找时并不是像字典一样简单的通过key去查找value,而是通过红黑树查找unordered_map的方式去查找的
    • ❗️最后,被关联对象指针地址反转作为key,ObjectAssociationMap作为value,映射成一个AssociationsHashMapc++类,查找时并不是像字典一样简单的通过key去查找value,而是通过哈希查找map的方式去查找的
    • ❗️具体的代码实现思路见注释
    关联对象本质图.png

    根据传入的关联对象value和关联策略policy,调用对应的retain/copy方法

    static id acquireValue(id value, uintptr_t policy) {
        switch (policy & 0xFF) {
        case OBJC_ASSOCIATION_SETTER_RETAIN:
            return objc_retain(value);
        case OBJC_ASSOCIATION_SETTER_COPY:
            return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
        }
        return value;
    }
    

    相关文章

      网友评论

          本文标题:关联对象

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