前言
我们知道一个对象即将释放的时候会进入到dealloc方法中,通常也是通过dealloc是否回调来检测循环引用。我一直对dealloc内部做了哪些事情十分好奇,今天就来探究下它内部的实现机制。
源码探究
苹果的内存管理是通过引用计数实现的,当一个对象的引用计数为0时就去释放这个对象,此时dealloc触发。在触发dealloc之前,肯定是先对一个对象进行了release操作,我们先来看下release源码。
- (oneway void)release {
((id)self)->rootRelease();
}
内部只是简单的调用了c++类的rootRelease方法,直接来到最终的调用方法。
ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
if (isTaggedPointer()) return false;
bool sideTableLocked = false;
isa_t oldisa;
isa_t newisa;
... //省略中间的无关代码
// 这里是准备触发dealloc的逻辑,请关注下面的代码
if (slowpath(newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
// does not actually return
}
newisa.deallocating = true;
if (slowpath(sideTableLocked)) sidetable_unlock();
__sync_synchronize();
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
}
return true;
}
为了方便阅读,我去除了中间无关触发dealloc的逻辑。首先判断该对象是否正在析构,如果是,则表明该对象过度释放,程序崩溃报错,一般在mrc时代容易出现过度释放的问题。如果不是正在析构,则标记该对象的deallocting为true。接下来通过objc_msgSend方法去调用dealloc方法,按理来说通过objc_msgSend调用方法,如果在子类中找到了sel,那么父类的dealloc不会调用,在我实际测试中,dealloc的调用顺序是子类->父类->根类(NSObject)
,查阅资料发现是编译器插入相关代码,向父类调用dealloc,所以在dealloc中不需要[super dealloc]就能完成整个继承链的dealloc。
接下来看NSObject中dealloc的源码。
- (void)dealloc {
_objc_rootDealloc(self);
}
void _objc_rootDealloc(id obj)
{
assert(obj);
obj->rootDealloc();
}
inline void objc_object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
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);
}
}
可以看到执行过程比较简单,_objc_rootDealloc -> rootDealloc -> objc_object::rootDealloc,在objc_object::rootDealloc中判断对象的几个条件:
- isa.nonpointer,32位系统和64位系统isa的结构不同,为0表示isa直接指向对象的class,为1表示isa不是直接指向class
- 是否有注册weak引用表
- 是否有association关联属性
- 是否有c++析构
- 是否有引用计数表。
我们来看下object_dispose中实现。
id object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
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;
}
在object_dispose中调用objc_destructInstance主要做了以下操作:
- object_cxxDestruct,这里主要是用于释放对象的实例变量
- _object_remove_assocations,移除掉所有关联属性,即通过objc_setAssociatedObject添加的关联属性
- clearDeallocating,先清空weak变量表且将所有weak引用指向nil,然后清空引用计数表。
最后通过free函数清除对象内存。