美文网首页
类扩展&分类关联对象底层探究

类扩展&分类关联对象底层探究

作者: 猿人 | 来源:发表于2020-10-28 00:53 被阅读0次

    一、分类及扩展

    分类又名 Category、类别
    • 分类中原则上只能添加方法。不能增加成员变量。
    • 分类中可以访问原来类中的成员变量,但是只能访问@protect和@public形式的变量
    • 分类中可以用@property 声明变量,只会生成变量的setter、getter方法的声明,不能生成方法实现 和 带下划线的成员变量
    • 可以通过runtime 给分类添加属性,即属性关联,重写setter、getter方法
    • 如果分类中有和原有类同名的方法,会优先调用分类中的方法,就是说会忽略原有类的方法。
    • 如果多个分类中都有同名方法 那么他的调用顺序和编译顺序有关系
      可以在 bulid Phases -> Compile Sources中查看顺序 由下而上调用
    • 如果继承关系 子类 和父类 和父类分类拥有同名方法 子类调用 父类和父类分类(不管父类有无实现)均不会响应。 如子类并未实现 父类 和父类分类 有同名方法 子类发起调用 则会 调用父类分类方法 父类分类方法(不管父类有无实现)父类分类调用顺序依旧和编译顺序有关
    类扩展又名 Extension
    • 类扩展不仅可以增加方法, 可以增加成员变量及属性,只是全部为私有的。
    • 可以说成特殊的分类,也可称之为 匿名分类。
    类扩展探究

    类扩展创建方式

    • 直接在类中写,向下面我们熟悉的味道,永远在实现之前声明之后


      截屏2020-10-27 下午2.44.19.png
    • 手动创建一个.h文件单拎出来。通过 command+N 新建 -> Objective-C File -> 选择Extension 截屏2020-10-27 下午2.47.05.png
    类扩展底层原理探索

    写一个类扩展并通过clang查看其源码

    clang -rewrite-objc main.mm -o main.cpp
    
    截屏2020-10-27 下午3.06.08.png
    截屏2020-10-27 下午3.10.14.png 截屏2020-10-27 下午3.15.26.png
    • 查看 LGTeacher 类拓展的方法,在编译过程中,方法就直接添加到了 methodlist中,作为类的一部分,即编译时期直接添加到本类里面

    • 运行objc源码程序 在readClass写上针对性LGTeacher的代码


      截屏2020-10-27 下午3.30.56.png

    总结

    • 由此可以看出 类扩展 在编译期 就会作为类的一部分,和类一起编译进来
    • 类扩展只是声明,且需要依赖当前的主类。

    分类关联对象底层探究

    截屏2020-10-27 下午3.51.40.png
    • objc_setAssociatedObject源码实现


      截屏2020-10-27 下午4.04.33.png
    • _object_set_associative_reference
    void
    _object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
    {
        // This code used to work when nil was passed for object and key. Some code
        // probably relies on that to not crash. Check and handle it explicitly.
        // rdar://problem/44094390
        if (!object && !value) return;
    
        if (object->getIsa()->forbidsAssociatedObjects())
            _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
    /// 将object 伪装成 DisguisedPtr 类型数据结构
        DisguisedPtr<objc_object> disguised{(objc_object *)object};
    /// 将策略 和 value 存入 ObjcAssociation 数据结构中
        ObjcAssociation association{policy, value};
    
        // retain the new value (if any) outside the lock.
        association.acquireValue();
    /// 局部作用域
        {
    ///初始化一个关联 管理对象  在构造时加锁,析构时开锁
            AssociationsManager manager;
     
    ///   AssociationsHashMap 类型 就是一个嵌套的DenseMap 
    ///DenseMap<DisguisedPtr<objc_object>, DenseMap<const void *, ObjcAssociation>>
    ///   manager.get() :
    /// 获取的是一个 static的_mapStorage变量 associations数据为manager.get()
    ///获取而来 所以associations是全局静态变量
            AssociationsHashMap &associations(manager.get());
    
            if (value) {
    //    try_emplace:         在这disguised作为key查找,如果已经在associations表中,就把查
    /// 找到的桶作为DenseMapIterator的位置指针进行初始化,然后用pair包装后返回;key
    ///没在associations表中就把disguised作为key,ObjectAssociationMap{}作为value存入
    ///桶中,然后把该桶作为DenseMapIterator的位置指针进行初始化,然后用pair包装后
    ///返回。返回值类型std::pair<DenseMapIterator, bool>
                auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
                if (refs_result.second) {
                    //能进来这里,说明disguised为key的桶是新插入进来的,所以根据条件设置isa_t中的has_assoc位为true
                    object->setHasAssociatedObjects();
                }
    
                //找到associations中的disguised对应的ObjectAssociationMap表
                auto &refs = refs_result.first->second;
                //用key在ObjectAssociationMap表中查找,如果表中不存在该key那么就把key和association对应插入到ObjectAssociationMap中
                auto result = refs.try_emplace(key, std::move(association));
                //result.second为false, 说明ObjectAssociationMap表中原来已有该key,不会移动,所以这里进行了swap的操作来交换association的值。
                if (!result.second) {
                    association.swap(result.first->second);
                }
            } else {   //value为nil, 取消关联。
            //先从associations表中找到disguised对应的ObjectAssociationMap表,用pair包装后返回。
                auto refs_it = associations.find(disguised);
                //如果从associations表中找到了disguised对应的ObjectAssociationMap表
                if (refs_it != associations.end()) {
                    //从pair中拿到ObjectAssociationMap表
                    auto &refs = refs_it->second;
                    //从ObjectAssociationMap表中查找key对应的association,然后把它作为DenseMapIterator的位置指针初始化后返回
                    auto it = refs.find(key);
                    //如果找到了就进去
                    if (it != refs.end()) {
                        //这里交换值是为了把要擦除的association记录下来,因为下面还要进行releaseHeldValue
                        association.swap(it->second);
                        //从ObjectAssociationMap表中擦除association以及其他相应的操作
                        refs.erase(it);
                        if (refs.size() == 0) {
                            //说明没有关联的值了,从associations表中擦除ObjectAssociationMap表
                            associations.erase(refs_it);
                        }
                    }
                }
            }
        }
    
        // release the old value (outside of the lock).//释放
        association.releaseHeldValue();
    }
     
    
    • AssociationsManager源码结构
    // class AssociationsManager manages a lock / hash table singleton pair.
    // Allocating an instance acquires the lock
    
    class AssociationsManager {
        using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
        static Storage _mapStorage;
    
    public:
        AssociationsManager()   { AssociationsManagerLock.lock(); }///初始化 加锁
        ~AssociationsManager()  { AssociationsManagerLock.unlock(); }///析构 解锁
    
        AssociationsHashMap &get() { 
            return _mapStorage.get();
        }
    
        static void init() {
            _mapStorage.init();
        }
    }
    

    从中可以看出get() 方法获取的为全局静态变量 唯一的

    • AssociationsHashMap &associations() 源码结构
    class ObjcAssociation {
        uintptr_t _policy;
        id _value;
    .....省略
    }
    
    typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;
    typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
      
    
    截屏2020-10-27 下午10.25.39.png
    • try_emplace
    2251862-56319ac00d5108f6.jpg
    • iterator
      截屏2020-10-27 下午11.14.44.png
      截屏2020-10-27 下午11.16.39.png
      从这里可以看出 返回值 pair<iterator,bool>类型 可以拆解为
      pair<KeyT,ValueT,ValueInfoT,KeyInfoT,BucketT,false ,Bool>
      pair<KeyT,ValueT,DenseMapValueInfo<ValueT>,DenseMapInfo<KeyT>,detail::DenseMapPair<KeyT, ValueT>,false ,Bool>
    截屏2020-10-27 下午10.59.33.png

    优化打印值

    ///1.KeyT
    //pair<objc::DenseMapIterator<DisguisedPtr<objc_object>,
    
    ///2.ValueT
    //objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>,    objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> >,
    
    ///3.DenseMapValueInfo<ValueT>    ->      ValueInfoT
    //objc::DenseMapValueInfo<objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >,
    
    ///4.DenseMapInfo<KeyT>      ->     KeyInfoT
    //objc::DenseMapInfo<DisguisedPtr<objc_object> >,
    
    ///5.detail::DenseMapPair<KeyT, ValueT>  ->      BucketT
    //objc::detail::DenseMapPair<DisguisedPtr<objc_object>, objc::DenseMap<const void *, objc::ObjcAssociation, objc::DenseMapValueInfo<objc::ObjcAssociation>, objc::DenseMapInfo<const void *>, objc::detail::DenseMapPair<const void *, objc::ObjcAssociation> > >,
    
    ///6.false
    //false>,
    
    ///7.bool
    //bool>
    

    到这里可以得出下面的一张关系图


    关联对象结构 2.jpg

    objc_getAssociatedObject源码分析

    id objc_getAssociatedObject(id object, const void *key)
    {
        return _object_get_associative_reference(object, key);
    }
    
    id _object_get_associative_reference(id object, const void *key)
    {   
        //先初始化一个用来接收值的association
        ObjcAssociation association{};
        {
            AssociationsManager manager;
            AssociationsHashMap &associations(manager.get());
            //用object作为key从associations表中找到对应的ObjectAssociationMap表
            AssociationsHashMap::iterator i = associations.find((objc_object *)object);
            if (i != associations.end()) {
                ObjectAssociationMap &refs = i->second;
                //用key在ObjectAssociationMap表中搜索对应的ObjcAssociation
                ObjectAssociationMap::iterator j = refs.find(key);
                if (j != refs.end()) {
                    //找到后赋值给association,然后retain
                    association = j->second;
                    association.retainReturnedValue();
                }
            }
        }
    
        return association.autoreleaseReturnedValue();
    }
    

    关联对象释放
    通过_objc_rootDealloc->rootDealloc->object_dispose->objc_destructInstance

    void *objc_destructInstance(id obj) 
    {
        if (obj) {
            // Read all of the flags at once for performance.
            bool cxx = obj->hasCxxDtor();
            bool assoc = obj->hasAssociatedObjects();
    
            // This order is important.
            if (cxx) object_cxxDestruct(obj);
            //如果有关联对象,移除
            if (assoc) _object_remove_assocations(obj);
            obj->clearDeallocating();
        }
        return obj;
    }
    
    void _object_remove_assocations(id object)
    {
        ObjectAssociationMap refs{};
        {
            AssociationsManager manager;
            AssociationsHashMap &associations(manager.get());
            AssociationsHashMap::iterator i = associations.find((objc_object *)object);
            if (i != associations.end()) {
                refs.swap(i->second);
                associations.erase(i);
            }
        }
        // release everything (outside of the lock).
        for (auto &i: refs) {
            i.second.releaseHeldValue();
        }
    }
    

    从中可以看出关联对象不用手动移除,在对象释放时会自动移除。

    总结

    • 类扩展是在编译期就已经是类的一部分了。一般用于对外隐藏属性和方法,但并不是真正的私有。
    • 关联对象通过manager维护了以伪装后的objc_object指针为key的AssociationsHashMap表和以const void *类型的指针为key的ObjectAssociationMap表。ObjectAssociation就存在ObjectAssociationMap表中。
    • 关联对象在对象释放时会自动移除。

    相关文章

      网友评论

          本文标题:类扩展&分类关联对象底层探究

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