美文网首页iOS 进阶
Objc对象的销毁过程

Objc对象的销毁过程

作者: CoderLS | 来源:发表于2018-11-15 14:06 被阅读42次

    销毁的源头

    调用-release,release会调用:
    uintptr_t objc_object::sidetable_release(bool performDealloc)

    sidetable_release():

    以下是runtime源码 NSObject.mm 1584行

    bjc_object::sidetable_release(bool performDealloc)
    {
    #if SUPPORT_NONPOINTER_ISA
        assert(!isa.nonpointer);
    #endif
        SideTable& table = SideTables()[this];
    
        bool do_dealloc = false;
        //加锁
        table.lock();
    //获取当前对象所在的sidetable(一个hash表),在sidetable.refcnts(RefcountMap,一个map)中查到当前对象的迭代器
        RefcountMap::iterator it = table.refcnts.find(this);
        //接着判断迭代器是否是指向了sidetable的end
        //如果是就代表找不到:
        if (it == table.refcnts.end()) {
          //将对象标记为“正在析构”
          //标记需要dealloc
            do_dealloc = true;
            table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
        } else if (it->second < SIDE_TABLE_DEALLOCATING) {
          //判断之前存储的引用计数值是否为 0,避免负数
          //将对象标记为“正在析构”
          //标记需要dealloc
            // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
            do_dealloc = true;
            it->second |= SIDE_TABLE_DEALLOCATING;
        } else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
           //将引用计数减一
            it->second -= SIDE_TABLE_RC_ONE;
        }
       //解锁
        table.unlock();
        if (do_dealloc  &&  performDealloc) {
        //调用对象的dealloc方法:
            ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
        }
        return do_dealloc;
    }
    

    如果调用了dealloc:

    进行以下过程时,不能再创建有新的 __weak引用,否则会crash

    递归调用父类的-dealloc

    如果是 MRC 代码,则需要手动释放实例变量

    最后调用NSObject的dealloc

    NSObject的dealloc会调用 _objc_rootDealloc(self);

    _objc_rootDealloc(id obj) NSObject.mm 1838行

    _objc_rootDealloc(id obj)
    {
        //是否还活着 断言
        assert(obj);
       //调用 objc_object::rootDealloc()
        obj->rootDealloc();
    }
    

    objc_object::rootDealloc() objc_object.h 415行

    objc_object::rootDealloc()
    {
        //是否使用TaggedPointer优化 是直接人突然
        if (isTaggedPointer()) return;  // fixme necessary?
    
       //不需要处理object_dispose的所有内容
        if (fastpath(isa.nonpointer  &&  
                     !isa.weakly_referenced  &&  
                     !isa.has_assoc  &&  
                     !isa.has_cxx_dtor  &&  
                     !isa.has_sidetable_rc))
        {
            assert(!sidetable_present());
            free(this);
        } 
        else {
           //调用object_dispose
            object_dispose((id)this);
        }
    }
    

    object_dispose(id obj) objc-runtime-new.mm 6313行

    id object_dispose(id obj)
    {
        //是否为nil 直接返回
        if (!obj) return nil;
        //调用 void *objc_destructInstance(id obj)
        objc_destructInstance(obj);    
        free(obj);
        return nil;
    }
    

    void *objc_destructInstance(id obj) objc-runtime-new.mm 6290行

    void *objc_destructInstance(id obj) 
    {
    //如果不为nil 才处理
        if (obj) {
            //是否有析构函数  这个bool值取决于当前类以及父类往上是否有实例变量,如果有实例变量当前类就有.cxxDestruct,当前类或父类有此方法值=YES,都没有才=NO
            bool cxx = obj->hasCxxDtor();
           //是否有关联对象
            bool assoc = obj->hasAssociatedObjects();
            // This order is important.
          //如果有析构函数 调用 void object_cxxDestruct(id obj)
            if (cxx) object_cxxDestruct(obj);
           //如果有关联对象 移除关联对象
            if (assoc) _object_remove_assocations(obj);
            //调用objc_clear_deallocating()清空引用计数表
            obj->clearDeallocating();
        }
        return obj;
    }
    

    void object_cxxDestruct(id obj) objc-class.mm 473行

    void object_cxxDestruct(id obj)
    {
       //如果为nil 直接retun
        if (!obj) return;
       //是否使用TaggedPointer优化 是直接return
        if (obj->isTaggedPointer()) return;
       //调用 static void object_cxxDestructFromClass(id obj, Class cls)
        object_cxxDestructFromClass(obj, obj->ISA());
    }
    

    static void object_cxxDestructFromClass(id obj, Class cls) objc-class 447行

    static void object_cxxDestructFromClass(id obj, Class cls)
    {
        void (*dtor)(id);
    
        // Call cls's dtor first, then superclasses's dtors.
    
        //往父类递归调用.cxxDestruct 直到hasCxxDtor=NO return结束
        for ( ; cls; cls = cls->superclass) {
            if (!cls->hasCxxDtor()) return; 
            dtor = (void(*)(id))
                lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
            if (dtor != (void(*)(id))_objc_msgForward_impcache) {
                if (PrintCxxCtors) {
                    _objc_inform("CXX: calling C++ destructors for class %s", 
                                 cls->nameForLogging());
                }
                (*dtor)(obj);
            }
        }
    }
    
    

    .cxx_destruct

    ARC下拥有实例变量才会有这个方法,通过Clang CodeGen生成,MRC都需要手动release所以不需要
    ARC下会遍历当前对象所有的实例变量通过objc_storeStrong() release掉
    具体实现过程:https://blog.sunnyxx.com/2014/04/02/objc_dig_arc_dealloc/

    void objc_removeAssociatedObjects(id object) objc-runtime 635行

    void objc_removeAssociatedObjects(id object) 
    {
        if (object && object->hasAssociatedObjects()) {
            _object_remove_assocations(object);
        }
    }
    

    void _object_remove_assocations(id object) objc-references 316行

    关联对象都存放在AssociationsHashMap中,以obj为key,以存放关联对象的ObjectAssociationMap为value,然后拿到ObjectAssociationMap中的所有ObjcAssociation对象,然后此对象调用ReleaseValue(),继而调用releaseValue,然后调用objc_release
    void _object_remove_assocations(id object) {
        vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
        {
            AssociationsManager manager;
            AssociationsHashMap &associations(manager.associations());
            if (associations.size() == 0) return;
            disguised_ptr_t disguised_object = DISGUISE(object);
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // copy all of the associations that need to be removed.
                ObjectAssociationMap *refs = i->second;
                for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                    elements.push_back(j->second);
                }
                // remove the secondary table.
                delete refs;
                associations.erase(i);
            }
        }
        // the calls to releaseValue() happen outside of the lock.
        for_each(elements.begin(), elements.end(), ReleaseValue());
    }
    
    struct ReleaseValue {
        void operator() (ObjcAssociation &association) {
            releaseValue(association.value(), association.policy());
        }
    };
    static void releaseValue(id value, uintptr_t policy) {
        if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
            return objc_release(value);
        }
    }
    

    void objc_clear_deallocating(id obj) NSObject.mm 1705行

    void 
    objc_clear_deallocating(id obj) 
    {
        assert(obj);
    
        if (obj->isTaggedPointer()) return;
        obj->clearDeallocating();
    }
    

    objc_object::clearDeallocating() objc-object.h 399行

    inline void 
    objc_object::clearDeallocating()
    {
        if (slowpath(!isa.nonpointer)) {
            // Slow path for raw pointer isa.
           //调用sidetable_clearDeallocating()把对象的weak指针置nil,把对象的计数引用移除
            sidetable_clearDeallocating();
        }
        else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
            /判断是否有过弱引用   是否因为计数太大有多个sidetable
            // Slow path for non-pointer isa with weak refs and/or side table data.
           //调用clearDeallocating_slow();内部再分开判断各自实现sidetable_clearDeallocating的内容
            clearDeallocating_slow();
        }
    
        assert(!sidetable_present());
    }
    
    相信看了上面的源码分析 对于下面这种图 父类有属性,关联对象,子类有属性关联对象,此时创建个[SubClass new]然后释放掉,这几个类的dealloc打印顺序是什么应该也清楚了,当然属性之间dealloc顺序,关联对象之间顺序还需要注意,同一个类多个属性的dealloc顺序是倒序,
    • 属性列表的顺序:

    分类属性1,分类属性2,类扩展属性1,类扩展属性2,类属性1,类属性2

    • 属性的dealloc销毁顺序是:

    类扩展属性2,类扩展属性1,类属性2,类属性1,然后再销毁父类的属性

    关联对象添加的成员变量是先销毁子类的成员变量,在销毁父类的,在同一个类里添加的顺序不一定,因为底层是存储在无序map中,所以dealloc顺序不一定

    屏幕快照 2018-11-15 下午2.00.48.png

    runtime源代码下载地址
    搜索objc4 选择数字最大的即为最新的(并不是最下面的就是最新的,看数字大小)

    相关文章

      网友评论

        本文标题:Objc对象的销毁过程

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