大家都知道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中會執行上面那一大坨方法來釋放這個執行個體對象,就是這樣。