本文源自本人的學(xué)習(xí)記錄整理與理解,其中參考閱讀了部分優(yōu)秀的博客和書(shū)籍,盡量以通俗簡(jiǎn)單的語(yǔ)句轉(zhuǎn)述。引用到的地方如有遺漏或未能一一列舉原文出處還望見(jiàn)諒與指出,另文章內(nèi)容如有不妥之處還望指教,萬(wàn)分感謝 !

自動(dòng)釋放池是什么嘞 ?
自動(dòng)釋放池:即@autoreleasepool,通過(guò)AutoreleasePoolPage來(lái)管理調(diào)用了autorelease方法的對(duì)象,把該對(duì)象在合適的時(shí)機(jī)釋放掉,這就是自動(dòng)釋放池
自動(dòng)釋放池的主要底層數(shù)據(jù)結(jié)構(gòu)是:__AtAutoreleasePool、AutoreleasePoolPage
release 和 drain的區(qū)別
當(dāng)我們向自動(dòng)釋放池 pool 發(fā)送 release 消息,將會(huì)向池中臨時(shí)對(duì)象發(fā)送一條 release 消息,并且自身也會(huì)被銷毀
向它自動(dòng)釋放池發(fā)送drain消息時(shí),將會(huì)向池中臨時(shí)對(duì)象發(fā)送一條release消息同時(shí)在支持 GC 的系統(tǒng)中(Mac)可以引起 GC 回收操作,而 release 不可以
官方解釋
release:清空自動(dòng)釋放池且把自動(dòng)釋放池對(duì)象釋放。(ARC)
drain:清空自動(dòng)釋放池
- 原理:
define POOL_BOUNDARY nil ;nil值為0 ,是一個(gè)哨兵對(duì)象
假設(shè)還沒(méi)有創(chuàng)建AutoreleasePoolPage,
從第一個(gè)autorelease對(duì)象開(kāi)始調(diào)用push方法會(huì)將一個(gè)
POOL_BOUNDARY入棧(不是棧空間,而是數(shù)據(jù)結(jié)構(gòu)中的棧),并且返回POOL_BOUNDARY存儲(chǔ)的內(nèi)存地址調(diào)用push方法開(kāi)始存放
第一個(gè)autorelease對(duì)象的地址,返回存儲(chǔ)的地址調(diào)用pop方法時(shí)傳入一個(gè)
POOLBOUNDARY的內(nèi)存地址,會(huì)從最后一個(gè)入棧的對(duì)象開(kāi)始發(fā)送release消息,直到遇到這個(gè)POOL_BOUNDARYid*next 是查詢指針,指向下一個(gè)可以用來(lái)存放autorelease對(duì)象地址的區(qū)域,
POOL_BOUNDARY是一個(gè)標(biāo)記,代表一個(gè)自動(dòng)釋放池和另一個(gè)自動(dòng)釋放池的邊界,所以存取都需要
@autoreleasepool 最終會(huì)在大括號(hào)的開(kāi)始和結(jié)束生成以下兩行代碼:
@autoreleasepool {
//添加
atautoreleasepoolobj = objc_autoreleasePoolPush();
實(shí)現(xiàn)了autorelease的對(duì)象
//推出
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
直觀的代碼邏輯展示
@autoreleasepool {
atautoreleasepoolobj = objc_autoreleasePoolPush();
@autoreleasepool {
atautoreleasepoolobj = objc_autoreleasePoolPush();
@autoreleasepool {
atautoreleasepoolobj = objc_autoreleasePoolPush();
for ( 循環(huán) ) {
@autoreleasepool ........
}
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
從runtime源碼中找實(shí)現(xiàn)
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
NEVER_INLINE
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
push()
id *autoreleaseNewPage(id obj)
{
//創(chuàng)建
AutoreleasePoolPage *page = hotPage();
//把需要加入的對(duì)象添加(page->add(obj))進(jìn)來(lái)
if (page) return autoreleaseFullPage(obj, page);
else return autoreleaseNoPage(obj);
}
static inline void *push()
{
id *dest;
if (slowpath(DebugPoolAllocation)) {//當(dāng)前沒(méi)有pool page
// Each autorelease pool starts on a new pool page.
//傳入POOL_BOUNDARY,新創(chuàng)建一個(gè)pool page并把obj地址添加進(jìn)去
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {//直接傳入POOL_BOUNDARY,并把obj地址添加進(jìn)去
dest = autoreleaseFast(POOL_BOUNDARY);
}
ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
pop()
static inline void
pop(void *token)//token:POOL_BOUNDARY的地址
{
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// Popping the top-level placeholder pool.
拿到當(dāng)前頁(yè)釋放池
page = hotPage();
if (!page) {
// Pool was never used. Clear the placeholder.
return setHotPage(nil);
}
// Pool was used. Pop its contents normally.
// Pool pages remain allocated for re-use as usual.
拿到上一頁(yè)釋放池
page = coldPage();
獲取第一個(gè)存儲(chǔ)位
token = page->begin();
} else {
傳入POOL_BOUNDARY的地址,找到page
page = pageForPointer(token);
}
停止位
stop = (id *)token;
if (*stop != POOL_BOUNDARY) {
if (stop == page->begin() && !page->parent) {
// Start of coldest page may correctly not be POOL_BOUNDARY:
// 1. top-level pool is popped, leaving the cold page in place
// 2. an object is autoreleased with no pool
} else {
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
return badPop(token);
}
}
if (slowpath(PrintPoolHiwat || DebugPoolAllocation || DebugMissingPools)) {
return popPageDebug(token, page, stop);
}
從最后一個(gè)入棧的對(duì)象開(kāi)始發(fā)送release消息,直到遇到這個(gè)POOL_BOUNDARY返回
return popPage<false>(token, page, stop);
}
AutoreleasePoolPage結(jié)構(gòu)

每個(gè)AutoreleasePoolPage對(duì)象占用
4096字節(jié)內(nèi)存,除了用來(lái)存儲(chǔ)它內(nèi)部成員變量占用56字節(jié)外,剩下的空間用來(lái)存放autorelease對(duì)象的地址
如果內(nèi)存不夠了,就會(huì)再創(chuàng)建一個(gè)新的AutoreleasePoolPage對(duì)象接著存所有的AutoreleasePoolPage對(duì)象是通過(guò)
雙向鏈表的形式連接在一起,第一頁(yè)的上一頁(yè)是0,最后一頁(yè)的下一頁(yè)是0
雙向鏈表:第一個(gè)對(duì)象可以通過(guò)指針一個(gè)一個(gè)找到最后一個(gè)對(duì)象,最后一個(gè)對(duì)象也可以通過(guò)指針一個(gè)一個(gè)找到第一個(gè)對(duì)象,這種結(jié)構(gòu)的鏈表被認(rèn)為是雙向鏈表;
緣分一道橋設(shè)計(jì)思路:
一條鎖鏈搭建的吊橋,兩條粗壯的鎖鏈;假設(shè)建造過(guò)程是這樣的:一條從第一塊橋板的左邊穿過(guò),一直到最后一塊木板到達(dá)對(duì)岸,另一條從最后一塊橋板開(kāi)始往回穿過(guò),一直到回到第一塊橋板的右邊;這個(gè)過(guò)程不就像極了雙向鏈表嘛 ????????????????

自動(dòng)釋放池與RunLoop有啥關(guān)系 ?
先看看主線程的Runloop做了什么
RunLoop活動(dòng)狀態(tài)
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), == 1
kCFRunLoopBeforeTimers = (1UL << 1), == 2
kCFRunLoopBeforeSources = (1UL << 2), == 4
kCFRunLoopBeforeWaiting = (1UL << 5), == 32
kCFRunLoopAfterWaiting = (1UL << 6), == 64
kCFRunLoopExit = (1UL << 7), == 128
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
*/
/*
kCFRunLoopEntry push
<CFRunLoopObserver>{
valid = Yes,
activities = 0x1, 活動(dòng)狀態(tài)值 0x1 = 1 --> kCFRunLoopEntry
repeats = Yes,
order = -2147483647,
callout = _wrapRunLoopWithAutoreleasePoolHandler (0x103376df2), context = {type = mutable-small, count = 1, values = (t0 : <0x7fd0bf802048>)}
}
kCFRunLoopBeforeWaiting | kCFRunLoopExit
kCFRunLoopBeforeWaiting pop、push
kCFRunLoopExit pop
<CFRunLoopObserver>{
valid = Yes,
活動(dòng)狀態(tài)值 0xa0 == 160 = 32 + 128 ; --> kCFRunLoopBeforeWaiting | kCFRunLoopExit
activities = 0xa0,
repeats = Yes,
order = 2147483647,
callout = _wrapRunLoopWithAutoreleasePoolHandler (0x103376df2), context = {type = mutable-small, count = 1, values = (t0 : <0x7fd0bf802048>)
}
}
梳理一下:
activities = 0x1
活動(dòng)狀態(tài)值 0x1 = 1 --> kCFRunLoopEntry
activities= 0xa0
活動(dòng)狀態(tài)值 0xa0 == 160 = 32 + 128 ; --> kCFRunLoopBeforeWaiting | kCFRunLoopExit
kCFRunLoopEntry: 開(kāi)始進(jìn)入runloop
kCFRunLoopBeforeWaiting:runloop開(kāi)始休眠
kCFRunLoopExit:runloop退出
- 主線程的
runloop中注冊(cè)了2個(gè)Observer(監(jiān)聽(tīng)器) - 第1個(gè)Observer:
- 監(jiān)聽(tīng)
kCFRunLoopEntry事件,觸發(fā)就會(huì)調(diào)用objc_autoreleasePoolPush()函數(shù)
- 監(jiān)聽(tīng)
- 第2個(gè)Observer:
- 監(jiān)聽(tīng)
kCFRunLoopBeforeWaiting事件,觸發(fā)就會(huì)調(diào)用objc_autoreleasePoolPop()和objc_autoreleasePoolPush()函數(shù) - 監(jiān)聽(tīng)
kCFRunLoopExit事件,觸發(fā)就會(huì)調(diào)用objc_autoreleasePoolPop()
- 監(jiān)聽(tīng)
看一行代碼來(lái)對(duì)其進(jìn)行分析
XYHPerson *person = [[[XYHPerson alloc] init] autorelease];
- 這個(gè)Person什么時(shí)候調(diào)用release,是由RunLoop來(lái)控制的
- 它可能是在某次RunLoop循環(huán)中 ,在RunLoop休眠或退出之前調(diào)用了release
開(kāi)發(fā)中實(shí)現(xiàn)的對(duì)象調(diào)用atuorelease方法會(huì)加入到
main函數(shù)中的@atuoreleasepool嗎 ?
不會(huì),main函數(shù)中@atuoreleasepool是專門(mén)為main函數(shù)使用的;不會(huì)用作其他用途;
開(kāi)發(fā)中實(shí)現(xiàn)的對(duì)象調(diào)用atuorelease方法只會(huì)加入當(dāng)前自動(dòng)釋放池,如果當(dāng)前沒(méi)有自動(dòng)釋放池,會(huì)新創(chuàng)建一個(gè)添加