美文网首页
iOS底层学习:类的扩展和关联对象

iOS底层学习:类的扩展和关联对象

作者: FireStroy | 来源:发表于2020-11-25 23:11 被阅读0次

    类的扩展和分类

    1. category:分类、类别
    • 给类增加方法
    • 不能添加成员变量
    • 可以使用runtime给分类添加属性
    • 分类中添加的属性指挥生成对应的settergetter方法的声明,不能生成方法实现和带下划线的成员变量。
    1. extension:类扩展
    • 可以添加成员属性,属于私有。
    • 可以添加方法,属于私有。

    关于类的扩展,是放在类的声明之后类的实现之前。类的扩展里面放的多是私有方法和成员变量。类的扩展会在编译期就作为类的一部分写入到类信息中。另外,类的扩展只是一个声明,它依赖主类需要在主类内部完成实现。

    关联对象

    分类不能添加属性,它只是声明了settergetter方法,没有实现,那么就需要借助关联对象来实现这个功能。

    分类.h中
    @property (nonatomic, copy) NSString *name;
    
    分类.m中
    - (void)setCate_name:(NSString *)name{
        /**
         (对象,标识符,value, 策略)
         */
        objc_setAssociatedObject(self, "name", cate_name, OBJC_ASSOCIATION_COPY_NONATOMIC);
    }
    
    - (NSString *)name{
        return  objc_getAssociatedObject(self, "name");
    }
    

    关联对象源码探索

    关联对象如何运作的

    static void
    _base_objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    {
      _object_set_associative_reference(object, key, value, policy);
    }
    
    static ChainedHookFunction<objc_hook_setAssociatedObject> SetAssocHook{_base_objc_setAssociatedObject};
    
    objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
    {
        SetAssocHook.get()(object, key, value, policy);
    }
    

    关联对象设值底层调用的方法就是_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)

    _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
    {
        ...
        DisguisedPtr<objc_object> disguised{(objc_object *)object};
        ObjcAssociation association{policy, value};
    
        association.acquireValue();
        //工作代码块
        {代码块}
        association.releaseHeldValue();
    }
    

    传递进来的object被包装成了DisguisedPtr,对应的valuepolicy打包成了ObjcAssociation
    然后具体的操作放在了代码块中

        association.acquireValue();
    {
            AssociationsManager manager;
        
            AssociationsHashMap &associations(manager.get());
    
            if (value) {
                auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
                if (refs_result.second) {
                    /* it's the first association we make */
                    object->setHasAssociatedObjects();
                }
    
                /* establish or replace the association */
                auto &refs = refs_result.first->second; // 空的桶子
                auto result = refs.try_emplace(key, std::move(association));
                if (!result.second) {
                    association.swap(result.first->second);
                }
            } else {
                auto refs_it = associations.find(disguised);
                if (refs_it != associations.end()) {
                    auto &refs = refs_it->second;
                    auto it = refs.find(key);
                    if (it != refs.end()) {
                        association.swap(it->second);
                        refs.erase(it);
                        if (refs.size() == 0) {
                            associations.erase(refs_it);
    
                        }
                    }
                }
            }
        }
    
    • acquireValue() 更具传递进来的polic对值进行处理
    inline void acquireValue() {
            if (_value) {
                switch (_policy & 0xFF) {
                case OBJC_ASSOCIATION_SETTER_RETAIN:
                    _value = objc_retain(_value);
                    break;
                case OBJC_ASSOCIATION_SETTER_COPY:
                    _value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
                    break;
                }
            }
        }
    

    基本流程:
    1:创建一个 AssociationsManager 管理类。
    2:拿到一张全局唯一的:AssociationsHashMap
    3:判断是否插入的关联值value是否存在,存在走第4步,不存在则会移除关联对象。
    4:try_emplace,并创建一个空的 ObjectAssociationMap 去取查询的键值对:
    5:如果发现没有这个 key 就插入一个 空的 BucketT进去并返回true
    6:通过setHasAssociatedObjects方法标记对象存在关联对象即置isa指针的has_assoc属性为true
    7:用当前 policy 和 value 组成了一个 ObjcAssociation 替换原来 BucketT 中的空
    8:标记一下 ObjectAssociationMap 的第一次为 false

    相关文章

      网友评论

          本文标题:iOS底层学习:类的扩展和关联对象

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