各個執行緒 Autorelease 物件的記憶體管理

Joy_xx發表於2017-11-15

最近和 bestswifterkuailejim 搞了一套模擬面試,然後不管是應屆生還是工作兩三年的高階工程師都對下面這幾個問題比較懵逼,可能是開發中用到的不多,在這裡淺淺的討論下

  • Autoreleasepool 與 Runloop 的關係
  • ARC 下什麼樣的物件由 Autoreleasepool 管理
  • 子執行緒預設不會開啟 Runloop,那出現 Autorelease 物件如何處理?不手動處理會記憶體洩漏嗎?

針對第一個問題,比較容易理解,可以看一下:ibireme深入理解RunLoop,主執行緒預設為我們開啟 Runloop,Runloop 會自動幫我們建立Autoreleasepool,並進行Push、Pop 等操作來進行記憶體管理

第二個問題,ARC 下什麼樣的物件由 Autoreleasepool 管理呢?大多數人的回答是:“都會由 pool 進行管理”。其實並不是這樣的,對於普通的物件是由編譯器在合適的地方為我們 Realease 了。針對這個問題,我已經總結過:引用計數帶來的一次討論,是參考了經典的《iOS與OS X多執行緒和記憶體管理 》這本書。

針對第三個問題,感覺比較難以回答,需要很細緻的讀過 Runtime 、Autoreleasepool 的原始碼才可以。我也是參考了 StackOverFlow 的回答:does NSThread create autoreleasepool automaticly now?。我再來簡單闡述下,在子執行緒你建立了 Pool 的話,產生的 Autorelease 物件就會交給 pool 去管理。如果你沒有建立 Pool ,但是產生了 Autorelease 物件,就會呼叫 autoreleaseNoPage 方法。在這個方法中,會自動幫你建立一個 hotpage(hotPage 可以理解為當前正在使用的 AutoreleasePoolPage,如果你還是不理解,可以先看看 Autoreleasepool 的原始碼,再來看這個問題 ),並呼叫 page->add(obj)將物件新增到 AutoreleasePoolPage 的棧中,也就是說你不進行手動的記憶體管理,也不會記憶體洩漏啦!StackOverFlow 的作者也說道,這個是 OS X 10.9+和 iOS 7+ 才加入的特性。並且蘋果沒有對應的官方文件闡述此事,但是你可以通過原始碼瞭解。這裡張貼部分原始碼:

static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
    // No pool in place.
    // hotPage 可以理解為當前正在使用的 AutoreleasePoolPage。
    assert(!hotPage());

    // POOL_SENTINEL 只是 nil 的別名
    if (obj != POOL_SENTINEL  &&  DebugMissingPools) {
        // We are pushing an object with no pool in place, 
        // and no-pool debugging was requested by environment.
        _objc_inform("MISSING POOLS: Object %p of class %s "
                     "autoreleased with no pool in place - "
                     "just leaking - break on "
                     "objc_autoreleaseNoPool() to debug", 
                     (void*)obj, object_getClassName(obj));
        objc_autoreleaseNoPool(obj);
        return nil;
    }

    // Install the first page.
    // 幫你建立一個 hotpage(hotPage 可以理解為當前正在使用的 AutoreleasePoolPage
    AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
    setHotPage(page);

    // Push an autorelease pool boundary if it wasn't already requested.
    // POOL_SENTINEL 只是 nil 的別名,哨兵物件
    if (obj != POOL_SENTINEL) {
        page->add(POOL_SENTINEL);
    }

    // Push the requested object.
    // 把物件新增到 自動釋放池 進行管理
    return page->add(obj);
}複製程式碼

Reference

相關文章