天天看點

IOS開發底層dealloc釋放細節探究

大家都知道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源碼中的,看下最終釋放:

IOS開發底層dealloc釋放細節探究

可以看到,真正的釋放調用的是free,但是在此之前,需要判斷是不是tagPointer,如果是tagPointer,那麼需要判斷:

  1. 弱引用
  2. 關聯對象
  3. c++析構函數
  4. 引用計數表

如果這些都為false,那麼直接free。否則如果不是tagPointer,那麼調用

object_dispose(obj)---->objc_destructInstance(obj),然後調用:

IOS開發底層dealloc釋放細節探究

然後調用:obj->clearDeallocating()

IOS開發底層dealloc釋放細節探究

然後調用了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嗎,如下圖:

IOS開發底層dealloc釋放細節探究

是以,總結下,不管是arm64的tagPointer,還是普通的pointer,當其要銷毀一個對象的時候,總是做以下幾步操作:

  1. 删除這個對象所有關聯對象
  2. c++析構函數
  3. 删除引用計數表中obj對應的引用計數
  4. 将弱引用表中的弱引用指針置位nil

        當一個對象的強引用被銷毀的時候,其會調用objc_release(),然後一路調用,然後進行引用計數-1,當-1後引用計數為0了,那麼會調用dealloc方法,否則僅僅-1。然後dealloc中會執行上面那一大坨方法來釋放這個執行個體對象,就是這樣。