大家都知道ARC后是不能显示地调用dealloc方法,但是可以重载此方法。
突然考虑到如果父类有属性需要释放,而子类重写了此方法,
且重写子类dealloc时不能调用[super dealloc]会不会把父类的方法覆盖了
写了个测试类
@interface ClassA:NSObject
@end
@implementation ClassA
-(void)dealloc
{
NSLog(@"dealloc ClassA");
}
@end
@interface ClassB:ClassA
@end
@implementation ClassB
-(void)dealloc
{
NSLog(@"dealloc ClassB");
}
@end
//main方法内
ClassA* b = [[ClassB alloc] init];
b = nil;
输出结果为
2013-10-11 12:48:25.721 ARCDeallocTestDemo[2420:a0b] dealloc ClassB
2013-10-11 12:48:25.723 ARCDeallocTestDemo[2420:a0b] dealloc ClassA
结论:
虽然我们重写了父类的dealloc,但在编译后,编译器还是会调用到父类的dealloc,这是因为编译器会为我们插入[super dealloc],(可以查看汇编验证)所以,最终当引用计数为0的时候,的dealloc调用流程如下:
子类------>父类1------>父类2------->NSObject
最终释放的代码也是在NSObject源码中的,看下最终释放:
可以看到,真正的释放调用的是free,但是在此之前,需要判断是不是tagPointer,如果是tagPointer,那么需要判断:
- 弱引用
- 关联对象
- c++析构函数
- 引用计数表
如果这些都为false,那么直接free。否则如果不是tagPointer,那么调用
object_dispose(obj)---->objc_destructInstance(obj),然后调用:
然后调用:obj->clearDeallocating()
然后调用了sidetable_clearDeallocating();
void
objc_object::sidetable_clearDeallocating()
{
SideTable& table = SideTables()[this];
// clear any weak table items
// clear extra retain count and deallocating bit
// (fixme warn or abort if extra retain count == 0 ?)
table.lock();
RefcountMap::iterator it = table.refcnts.find(this);
if (it != table.refcnts.end()) {
if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
table.refcnts.erase(it);
}
table.unlock();
}
可以看到,目的是删除这个对象所对应的引用计数表以及删除weak表,可以看到是调用了 weak_clear_no_lock(&table.weak_table, (id)this);
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// zero out references
weak_referrer_t *referrers;
size_t count;
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry);
}
这里不就是将对应的指针置位nil吗,如下图:
所以,总结下,不管是arm64的tagPointer,还是普通的pointer,当其要销毁一个对象的时候,总是做以下几步操作:
- 删除这个对象所有关联对象
- c++析构函数
- 删除引用计数表中obj对应的引用计数
- 将弱引用表中的弱引用指针置位nil
当一个对象的强引用被销毁的时候,其会调用objc_release(),然后一路调用,然后进行引用计数-1,当-1后引用计数为0了,那么会调用dealloc方法,否则仅仅-1。然后dealloc中会执行上面那一大坨方法来释放这个实例对象,就是这样。