引用計數的存儲
- 在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
代碼,則該對象出了方法會立即釋放.