內存管理 - 引用計數 weak指針

引用計數的存儲

  • 在64bit中,引用計數可以直接存儲在優化過的isa指針中,也可能存儲在SideTable類中
  • refcnts是一個存放著對象引用計數的散列表
    image.png

    優化過的isa指針是一個union共用體.里面最后的19位存儲著對象的引用計數.如果這19位不夠存儲,共用體內的has_sidetable_rc成員變量的值會變成1.
    image.png

    image.png
  • NSObject.mm源碼 - 引用計數
uintptr_t
_objc_rootRetainCount(id obj)
{
    assert(obj);

    return obj->rootRetainCount();
}

inline uintptr_t 
objc_object::rootRetainCount()
{
    //是一個TaggedPointer 不是一個OC對象
    if (isTaggedPointer()) return (uintptr_t)this;

    sidetable_lock();
    isa_t bits = LoadExclusive(&isa.bits);
    ClearExclusive(&isa.bits);
    if (bits.nonpointer) {//優化過的isa指針,引用計數這樣獲取
        uintptr_t rc = 1 + bits.extra_rc;//19位夠存儲,直接取出引用計數 + 1
        if (bits.has_sidetable_rc) {//如果19位不夠存儲,has_sidetable_rc值為1
            rc += sidetable_getExtraRC_nolock();//從sidetable中獲取額外的引用計數累加
        }
        sidetable_unlock();
        return rc;//返回該對象的引用計數.
    }

    sidetable_unlock();
//沒有優化過的isa指針,直接從sidetable中獲取引用計數
    return sidetable_retainCount();
}

dealloc weak指針實現原理

  • 當一個對象要釋放時,會自動調用dealloc,接下的調用軌跡是
    dealloc
    _objc_rootDealloc
    rootDealloc
    object_dispose
    objc_destructInstance、free


    image.png

ARC工作內容:

  • LLVM編譯器生成 內存管理代碼 retain release
  • RunTime對弱引用進行置空 等操作.

自動釋放池

  • 自動釋放池的主要底層數據結構是:__AtAutoreleasePool、AutoreleasePoolPage
  • 調用了autorelease的對象最終都是通過AutoreleasePoolPage對象來管理的
  • 源碼分析
    clang重寫@autoreleasepool
    objc4源碼:NSObject.mm


    image.png
  • 每個AutoreleasePoolPage對象占用4096字節內存,除了用來存放它內部的成員變量,剩下的空間用來存放autorelease對象的地址
  • 所有的AutoreleasePoolPage對象通過雙向鏈表的形式連接在一起


    image.png

AutoreleasePoolPage的結構

  • 調用push方法會將一個POOL_BOUNDARY入棧,并且返回其存放的內存地址
  • 調用pop方法時傳入一個POOL_BOUNDARY的內存地址,會從最后一個入棧的對象開始發送release消息,直到遇到這個POOL_BOUNDARY
  • id *next指向了下一個能存放autorelease對象地址的區域

Runloop和Autorelease

  • iOS在主線程的Runloop中注冊了2個Observer
  • 第1個Observer監聽了kCFRunLoopEntry事件,會調用objc_autoreleasePoolPush()
  • 第2個Observer
    監聽了kCFRunLoopBeforeWaiting事件,會調用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
  • 監聽了kCFRunLoopBeforeExit事件,會調用objc_autoreleasePoolPop()

autorelease對象在什么時機會被調用release

當前所處的RunLoop休眠之前,或退出前會調用pop操作,來釋放autorelease對象.

方法里有局部對象, 出了方法后會立即釋放嗎

如果編譯器對該局部對象生成的內存管理代碼是autoreleasepool該對象不會立刻釋放,需要在RunLoop休眠之前,或退出前會調用pop操作,來釋放autorelease對象;如果編譯器對該局部對象生成的內存管理代碼是在方法結束插入relase代碼,則該對象出了方法會立即釋放.

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容