dealloc

LLVM 关于 dealloc 的说明

Clang 12 documentation

A class may provide a method definition for an instance method named dealloc. This method will be called after the final release of the object but before it is deallocated or any of its instance variables are destroyed. The superclass’s implementation of dealloc will be called automatically when the method returns.

dealloc 在最后 release 时调用,但此时实例变量( Ivars )并未释放,父类的 dealloc 会在子类的 dealloc 返回后调用。

The instance variables for an ARC-compiled class will be destroyed at some point after control enters the dealloc method for the root class of the class. The ordering of the destruction of instance variables is unspecified, both within a single class and between subclasses and superclasses.

实例变量会在 root class (根类)的 dealloc 中释放,一般来说就是 NSObjectdealloc 方法,释放顺序不确定。

dealloc 调用时机

聊聊dealloc

当对象调用 release 方法时会走到 sidetable_release 这个方法中,而 sidetable_release 这个方法会判断是否需要调用 dealloc 方法:

uintptr_t objc_object::sidetable_release(bool performDealloc)
{
#if SUPPORT_NONPOINTER_ISA
    assert(!isa.nonpointer);
#endif
    // 找到当前对象所对应的 SideTable
    SideTable& table = SideTables()[this];
    bool do_dealloc = false;
    table.lock();
    // 找到当前对象所对应的引用计数
    RefcountMap::iterator it = table.refcnts.find(this);
    if (it == table.refcnts.end()) {
        // 如果找不到所对应的应用计数,则表示可以执行 dealloc ,
			  // 同时设置对应的值为 SIDE_TABLE_DEALLOCATING
        do_dealloc = true;
        table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
    } else if (it->second < SIDE_TABLE_DEALLOCATING) {
        // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
        // 如果引用计数小于 SIDE_TABLE_DEALLOCATING ,则表示引用计数为 0 ,可以执行 dealloc
        do_dealloc = true;
        it->second |= SIDE_TABLE_DEALLOCATING;
    } else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
        // 引用计数减 1
        it->second -= SIDE_TABLE_RC_ONE;
    }
    table.unlock();
    // 进行释放操作,执行 dealloc
    if (do_dealloc  &&  performDealloc) {
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
    }
    return do_dealloc;
}

dealloc 有可能在任何线程调用,在最后一个调用 release 方法的线程中调用。

函数调用顺序: dealloc->_objc_rootDealloc->objc_object::rootDealloc->object_dispose->objc_destructInstance

- (void)dealloc {
    _objc_rootDealloc(self);
}
void _objc_rootDealloc(id obj)
{
    assert(obj);

    obj->rootDealloc();
}
inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?
		// 判断 isa 的各个标志位,确认是否需要进行快速释放。
    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((id)this);
    }
}
id object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    free(obj);

    return nil;
}
void *objc_destructInstance(id obj)
{
    if (obj) {
        Class isa_gen = _object_getClass(obj);
        class_t *isa = newcls(isa_gen);

        // Read all of the flags at once for performance.
        bool cxx = hasCxxStructors(isa);
        bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen);

        // This order is important.
        if (cxx) object_cxxDestruct(obj); // 1
        if (assoc) _object_remove_assocations(obj); // 2

        if (!UseGC) objc_clear_deallocating(obj); // 3
    }

    return obj;
}
  1. object_cxxDestruct 调用 C++ 析构器,释放实例变量;
  2. _object_remove_assocations 清除 Associated 对象;
  3. objc_clear_deallocating ARC 相关操作,清理 SideTableweak 设置为 nil 等。

ARC 下 dealloc 过程及 .cxx_destruct 的探究

ARC下dealloc过程及.cxx_destruct的探究

这篇文章对 .cxx_destruct 做了深入研究,包括 .cxx_destruct 如何释放实例变量,如何调用 [super dealloc] 。这两者都是由编译器帮我们完成,插入这部分的代码。