@autoreleasepool 編譯(clang)后會(huì)被轉(zhuǎn)換成:
__AtAutoreleasePool __autoreleasepool;
__AtAutoreleasePool是一個(gè)結(jié)構(gòu)體,定義如下:
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
__AtAutoreleasePool中包含一個(gè)構(gòu)造函數(shù)及一個(gè)析構(gòu)函數(shù):構(gòu)造函數(shù)調(diào)用 objc_autoreleasePoolPush() 并返回一個(gè) atautoreleasepoolobj 對(duì)象;而析構(gòu)函數(shù)將atautoreleasepoolobj 對(duì)象作為 objc_autoreleasePoolPop() 的入?yún)ⅰ?/p>
@autoreleasepool{}實(shí)際是下面兩行的替換:
void * atautoreleasepoolobj = objc_autoreleasePoolPush();
// do something
objc_autoreleasePoolPop(atautoreleasepoolobj);
先看objc_autoreleasePoolPush()和objc_autoreleasePoolPop()的實(shí)現(xiàn),在runtime源碼的NSObject.mm文件中:
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
objc_autoreleasePoolPush()和objc_autoreleasePoolPop()實(shí)際上分別調(diào)用的AutoreleasePoolPage的push()和pop(ctxt) 函數(shù)。
自動(dòng)釋放池的主要底層數(shù)據(jù)結(jié)構(gòu)是 __AtAutoreleasePool 和 AutoreleasePoolPage
AutoreleasePoolPage

在 AutoreleasePoolPage 定義的上方有一大段注釋自動(dòng)釋放池實(shí)現(xiàn):
線程的自動(dòng)釋放池是指針的棧,即自動(dòng)釋放池是棧結(jié)構(gòu)。
每個(gè)指針要么是要釋放的對(duì)象,要么是自動(dòng)釋放池邊界POOL_BOUNDARY(哨兵對(duì)象,表示一個(gè) autorelease pool 的邊界)。
釋放池 token 是一個(gè)指向這個(gè)釋放池 POOL_BOUNDARY的指針。當(dāng)釋放池被 pop 的時(shí)候,在這個(gè)哨兵對(duì)象后面添加的那些hotter對(duì)象都會(huì)被 release。
這個(gè)棧(即 autorelease pool)是一個(gè)以 page 為結(jié)點(diǎn)的雙向鏈表,這些 page 根據(jù)需要來添加或者刪除。
Thread-local storage(TLS,即線程局部存儲(chǔ))指向 hot page,這個(gè) hot page 是指最新添加的 autorelease 對(duì)象所在的那個(gè) page。
這里需要注意的是,棧上只存指針(就是對(duì)象的地址),對(duì)象本身是存在堆上的。
AutoreleasePoolPage定義
class AutoreleasePoolPage : private AutoreleasePoolPageData
{
friend struct thread_data_t;
public:
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MIN_SIZE; // size and alignment, power of 2
#endif
private:
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
static size_t const COUNT = SIZE / sizeof(id);
// EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is
// pushed and it has never contained any objects. This saves memory
// when the top level (i.e. libdispatch) pushes and pops pools but
// never uses them.
# define EMPTY_POOL_PLACEHOLDER ((id*)1)
# define POOL_BOUNDARY nil
...
}
AutoreleasePoolPage 繼承自 AutoreleasePoolPageData。
struct AutoreleasePoolPageData
{
magic_t const magic;
__unsafe_unretained id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
: magic(), next(_next), thread(_thread),
parent(_parent), child(nil),
depth(_depth), hiwat(_hiwat)
{
}
};
這些參數(shù)的意思如下:
magic_t const magic:16bytes,用來校驗(yàn)AutoreleasePoolPage結(jié)構(gòu)的完整性
__unsafe_unretained id next:8bytes,*指向的是AutoreleasePoolPage中下一個(gè)為空的內(nèi)存地址(新來的對(duì)象會(huì)存儲(chǔ)到next處,初始化時(shí)指向 begin())
pthread_t const thread:8bytes,指向當(dāng)前線程,保存了當(dāng)前頁所在的線程(一個(gè)AutoreleasePoolPage屬于一個(gè)線程,一個(gè)線程中可以有多個(gè)AutoreleasePoolPage)。
AutoreleasePoolPage * const parent:8bytes,指向父page,第一個(gè)page的parent為nil
AutoreleasePoolPage child:8bytes,*指向子page,最后一個(gè)page的child為nil
uint32_t const depth:4bytes,表示深度,從0開始
uint32_t hiwat: 4bytes,代表 high water mark,表示最大入棧數(shù)量標(biāo)記
COUNT :page里的對(duì)象個(gè)數(shù)
EMPTY_POOL_PLACEHOLDER :空池占位。
POOL_BOUNDARY :哨兵對(duì)象或邊界對(duì)象, nil別名,用來區(qū)別每個(gè) AutoreleasePool的邊界,用來區(qū)分不同的自動(dòng)釋放池,以解決自動(dòng)釋放池嵌套的問題。
PAGE_MAX_SIZE :定義的大小(在下面定義中可以看到是4096bytes)
SIZE:AutoreleasePoolPage的大小
define BYTE_SIZE 8 /* byte size in bits */
#define I386_PGBYTES 4096 / bytes per 80386 page /
#define PAGE_SIZE I386_PGBYTES
#define PAGE_MAX_SIZE PAGE_SIZE
define PAGE_MIN_SIZE PAGE_SIZE
一個(gè) AutoreleasePoolPage 會(huì)開辟 PAGE_MAX_SIZE 的內(nèi)存(4096 bytes,可能會(huì)根據(jù)不同設(shè)備及系統(tǒng)分配不同的內(nèi)存),除了 AutoreleasePoolPage 的成員變量所占空間(共 56 bytes),其余空間將會(huì)用來存儲(chǔ)加入到自動(dòng)釋放池的對(duì)象,一個(gè)AutoreleasePoolPage能存放505個(gè)對(duì)象(即(4096-56)/8 = 505) 。
一個(gè) AutoreleasePoolPage 初始狀態(tài)結(jié)構(gòu)如下:

begin() 和 end() 這兩個(gè)函數(shù)幫助快速獲取AutoreleasePoolPage存儲(chǔ)對(duì)象地址的起至位置。
next 指向了最新下一個(gè)為空的內(nèi)存地址,當(dāng) next == end() 時(shí),表示當(dāng)前 page 已經(jīng)滿了。
初始化一個(gè)AutoreleasePool后:

初始化一個(gè)AutoreleasePool會(huì)在AutoreleasePoolPage添加一個(gè)POOL_BOUNDARY,并將這個(gè)POOL_BOUNDARY返回,來確保在 pop 調(diào)用的時(shí)候,不會(huì)出現(xiàn)異常。POOL_BOUNDARY會(huì)存放在當(dāng)前 next 指向的位置,當(dāng)對(duì)象存放完成后,next 指針會(huì)指向下一個(gè)為空的地址。
向AutoreleasePool添加一個(gè)對(duì)象

next 原來指向的地址存入一個(gè) obj(id*,指向autorelease對(duì)象的指針),而next 指針則指向下一個(gè)為空的地址。
下面去源碼里驗(yàn)證一下
objc_autoreleasePoolPush 函數(shù)
首先自動(dòng)釋放池初始化,返回一個(gè)atautoreleasepoolobj哨兵對(duì)象:
** void * atautoreleasepoolobj = objc_autoreleasePoolPush();**
這里objc_autoreleasePoolPush()實(shí)現(xiàn)上面已經(jīng)寫出來了,里面返回的是AutoreleasePoolPage的push()函數(shù):
static inline void *push()
{
id *dest;
if (slowpath(DebugPoolAllocation)) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
dest = autoreleaseFast(POOL_BOUNDARY);
}
ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
push()函數(shù)通過調(diào)用 autoreleaseFast()函數(shù)(這里入?yún)OOL_BOUNDARY哨兵對(duì)象)來執(zhí)行具體的插入操作:
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
hotPage()是正在使用的AutoreleasePoolPage。
autoreleaseFast() 函數(shù)里面分為三種情況:
【1】當(dāng)前 hotPage存在且沒有滿時(shí):
調(diào)用 page->add(obj) 函數(shù)將對(duì)象加入該 hotPage 中,即 next 指向的位置;
【2】當(dāng)前hotpage 存在但是已滿時(shí):
調(diào)用 autoreleaseFullPage(obj, page) 函數(shù),該函數(shù)會(huì)先查找 hotPage 的 child,如果有則將 child page 設(shè)置為 hotPage,如果沒有則將創(chuàng)建一個(gè)新的 hotPage,之后在這個(gè)新的 hotPage 上執(zhí)行 **page->add(obj) **操作;
【3】當(dāng)前 page不存在時(shí):
調(diào)用** autoreleaseNoPage(obj) 函數(shù),該函數(shù)會(huì)創(chuàng)建一個(gè) hotPage,然后執(zhí)行 page->add(obj) **操作。
接下來看看 add() 函數(shù)的定義:
id *add(id obj)
{
ASSERT(!full());
unprotect();
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;
protect();
return ret;
}
add() 函數(shù)會(huì)把傳入的對(duì)象obj 存放在原本 next 所在的位置, 而next 指針移到下一個(gè)空位置。
再看看autoreleaseFullPage(obj, page),(當(dāng)前 hotPage 已滿):
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
// The hot page is full.
// Step to the next non-full page, adding a new page if necessary.
// Then add the object to that page.
ASSERT(page == hotPage());
ASSERT(page->full() || DebugPoolAllocation);
do {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
從autoreleaseFullPage的實(shí)現(xiàn)可以看出:
【1】當(dāng)前hotPage 已滿;
【2】從傳入的 page 開始遍歷整個(gè)雙向鏈表,直到查找到一個(gè)未滿的AutoreleasePoolPage;如果沒有找到未滿的AutoreleasePoolPage,則創(chuàng)建一個(gè)新的AutoreleasePoolPage。最后得到新的page;
【3】將上一步中的page設(shè)置為hotpage;
【4】調(diào)用add(obj),將obj對(duì)象添加的page中。
最后我們?cè)賮砜纯碼utoreleaseNoPage(obj),(沒有 hotPage):
id *autoreleaseNoPage(id obj)
{
// "No page" could mean no pool has been pushed
// or an empty placeholder pool has been pushed and has no contents yet
ASSERT(!hotPage());
if (obj == POOL_BOUNDARY) {
// We are pushing a pool with no pool in place,
// and alloc-per-pool debugging was not requested.
// Install and return the empty pool placeholder.
return setEmptyPoolPlaceholder();
}
// Install the first page.
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
// Push a boundary on behalf of the previously-placeholder'd pool.
if (pushExtraBoundary) {
page->add(POOL_BOUNDARY);
}
// Push the requested object or pool.
return page->add(obj);
}
這里對(duì)函數(shù)實(shí)現(xiàn)進(jìn)行了刪減只留下正常流程:
【1】當(dāng)前沒有可用的AutoreleasePoolPage;
【2】創(chuàng)建一個(gè)AutoreleasePoolPage并設(shè)置為 hotPage;
【3】條件性給這個(gè)hotPage添加一個(gè)POOL_BOUNDARY哨兵對(duì)象(這里條件不影響流程認(rèn)知,不做說明);
【4】然后執(zhí)行** page->add(obj) **操作。
不存在 AutoreleasePoolPage也就意味著沒有可用的自動(dòng)釋放池,就要從頭開始構(gòu)建這個(gè)自動(dòng)釋放池的雙向鏈表,也就是說,新的 AutoreleasePoolPage 是沒有 parent 指針的。
最后,看一下autorelease(id obj)實(shí)現(xiàn),同樣也調(diào)用了 autoreleaseFast(obj) 函數(shù):
static inline id autorelease(id obj)
{
ASSERT(obj);
ASSERT(!obj->isTaggedPointer());
id *dest __unused = autoreleaseFast(obj);
ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
AutoreleasePoolPage 的autorelease 函數(shù)的和 push 操作的實(shí)現(xiàn)非常相似。
只不過push操作插入的是一個(gè)POOL_BOUNDARY ,而 autorelease操作插入的是一個(gè)具體的autoreleased 對(duì)象,向一個(gè)對(duì)象發(fā)送 autorelease 消息,就會(huì)把該對(duì)象 add 進(jìn) page 里。
objc_autoreleasePoolPop(void *ctxt)
在調(diào)用時(shí) (atautoreleasepoolobj是要釋放的釋放池的哨兵對(duì)象)
objc_autoreleasePoolPop(atautoreleasepoolobj);
objc_autoreleasePoolPop的實(shí)現(xiàn)上面已經(jīng)列出,實(shí)際調(diào)用AutoreleasePoolPage的pop()函數(shù):
static inline void
pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
//token對(duì)用的自動(dòng)釋放池是一個(gè)空的占位池
}else{
**page =pageForPointer(token);**
}
stop = (id*)token;
return popPage<false>(token, page, stop);
}
page =pageForPointer(token); 和 return popPage(token, page, stop);
static AutoreleasePoolPage *pageForPointer(uintptr_tp)
{ AutoreleasePoolPage *result; uintptr_toffset = p %SIZE; ASSERT(offset >=sizeof(AutoreleasePoolPage)); result = (AutoreleasePoolPage*)(p - offset); result->fastcheck(); returnresult; }
這里 token 是一個(gè)指向這個(gè)釋放池 POOL_BOUNDARY的指針。
pageForPointer(token) 會(huì)獲取哨兵對(duì)象所在 AutoreleasePoolPage:主要是通過指針與 page 大小取模得到其偏移量(因?yàn)樗械?AutoreleasePoolPage 在內(nèi)存中都是對(duì)齊的),最后通過 fastCheck() 函數(shù)檢查得到的是不是一個(gè) AutoreleasePoolPage。
static void
popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
page->releaseUntil(stop);
if (page->child) {
// hysteresis: keep one empty child if page is more than half full
if (page->lessThanHalfFull()) {
page->child->kill();
}
else if (page->child->child) {
page->child->child->kill();
}
}
}
popPage()中調(diào)用的releaseUntil():
void releaseUntil(id *stop)
{
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
while (this->next != stop) {
// Restart from hotPage() every time, in case -release
// autoreleased more objects
AutoreleasePoolPage *page = hotPage();
// fixme I think this `while` can be `if`, but I can't prove it
while (page->empty()) {
page = page->parent;
setHotPage(page);
}
page->unprotect();
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
if (obj != POOL_BOUNDARY) {
objc_release(obj);
}
}
setHotPage(this);
}
popPage()函數(shù)首先調(diào)用 releaseUntil()函數(shù)循環(huán)釋放棧中的對(duì)象,直到 stop;releaseUntil()函數(shù)會(huì)先把 next 指針向前移動(dòng),取到將要釋放的一個(gè)指針,之后調(diào)用 memset 擦除該指針?biāo)純?nèi)存,再調(diào)用 objc_release 函數(shù)釋放該指針指向的對(duì)象,這樣通過 next 指針循環(huán)往前查找去釋放對(duì)象,期間可往前跨越多個(gè) page,直到找到傳進(jìn)來的哨兵對(duì)象為止。當(dāng)有嵌套的 autoreleasepool 時(shí),會(huì)清除一層后再清除另一層,因?yàn)?pop 是會(huì)釋放到上次 push 的位置為止,每次一層,互不影響。
popPage()函數(shù)中在releaseUntil()函數(shù)后面判斷,如果傳入的哨兵對(duì)象所在 page 有 child,有兩種情況:一是當(dāng)前 page 使用不滿一半,從 child page 開始將后面所有 page 都 kill();二是當(dāng)前 page 使用超過一半,從 child page 的 child page(即孫子,如果有的話)開始將后面所有的 page 都 kill()
kill()函數(shù)刪除雙向鏈表中的每一個(gè)的page,找到當(dāng)前page的 child 方向尾部 page,然后反向釋放并且把其parent節(jié)點(diǎn)的 child 指針置空。
void kill()
{
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
AutoreleasePoolPage *page = this;
while (page->child) page = page->child;
AutoreleasePoolPage *deathptr;
do {
deathptr = page;
page = page->parent;
if (page) {
page->unprotect();
page->child = nil;
page->protect();
}
delete deathptr;
} while (deathptr != this);
}
[NSObject autorelease]
- (id)autorelease
—id _objc_rootAutorelease(self);
——obj->rootAutorelease();
——— id objc_object::rootAutorelease()
————id objc_object::rootAutorelease2()
————— AutoreleasePoolPage::autorelease((id)this);
到這里我們發(fā)現(xiàn),一個(gè)對(duì)象autorelease,最終還是AutoreleasePoolPage的autorelease;
AutoreleasePoolPage的autorelease上面已經(jīng)介紹過。
總結(jié)
一、自動(dòng)釋放池是一個(gè)雙向鏈表,鏈表的每一個(gè)元素就是就是一個(gè)AutoreleasePoolPage。

二、釋放池主要通過 push 及 pop 操作來管理:
每調(diào)用一次 objc_autoreleasepoolpush 操作就會(huì)創(chuàng)建一個(gè)新的autoreleasepool ,往 AutoreleasePoolPage 中插入一個(gè)POOL_BOUNDARY ,并返回這個(gè)POOL_BOUNDARY的內(nèi)存地址。
當(dāng)銷毀一個(gè)自動(dòng)釋放池時(shí),會(huì)調(diào)用objc_autoreleasepoolpop()函數(shù)并傳入一個(gè)POOL_BOUNDARY,會(huì)從自動(dòng)釋放池中最后一個(gè)對(duì)象開始,依次給它們發(fā)送release消息,直到遇到這個(gè)POOL_BOUNDARY。
三、向一個(gè)對(duì)象發(fā)送 autorelease 消息,會(huì)把該對(duì)象 add 進(jìn) AutoreleasePoolPage 里
四、從main函數(shù)中知道iOS項(xiàng)目默認(rèn)是包裹在大的釋放池中;RunLoop開始循環(huán)、休眠和退出時(shí)會(huì)對(duì)釋放池進(jìn)行objc_autoreleasepoolpush/objc_autoreleasepoolpop操作。
五、線程和釋放池一一對(duì)應(yīng);
擴(kuò)展
一、autoreleasepool的使用場(chǎng)景?
使用場(chǎng)景:多次創(chuàng)建臨時(shí)變量導(dǎo)致內(nèi)存上漲時(shí),需要延遲釋放
蘋果的文檔有說大意如下:
1、程序不是基于 UI 框架的,如命令行工具(因?yàn)樗]有內(nèi)置的”autorelease pool”的支持)
2、你編寫的循環(huán)創(chuàng)建了大量的臨時(shí)對(duì)象(在循環(huán)體內(nèi)創(chuàng)建一個(gè)@autoreloeasePool{}在循環(huán)中使用autorelease pool block可以降低內(nèi)存峰值)
for (NSURL url in urls) {
@autoreleasepool {
/ Process the string, creating and autoreleasing more objects. */
}
}
3、如果創(chuàng)建了一個(gè)輔助線程。當(dāng)線程開始執(zhí)行的時(shí)候你必須立馬創(chuàng)建一個(gè)autorelease pool block,否則可能造成autoreleased對(duì)象的內(nèi)存泄漏。
4、使用容器的block版本的枚舉器時(shí),內(nèi)部會(huì)自動(dòng)添加一個(gè)AutoreleasePool:
[array enumerateObjectsUsingBlock:^(idobj,NSUIntegeridx,BOOL*stop) {
// 這里被一個(gè)局部@autoreleasepool包圍著
}];
二、autorelease 對(duì)象會(huì)在什么時(shí)候釋放?
分兩種情況:
我們自己添加的 @autoreleasepool:會(huì)在大括號(hào)結(jié)束時(shí)釋放(Any object (such as fileContents) sent an autorelease message inside the autorelease pool block is released at the end of the block.)
不使用 @autoreleasepool:不手動(dòng)指定 autoreleasepool 的 autorelease 對(duì)象出了作用域之后,會(huì)被添加到最近一次創(chuàng)建的自動(dòng)釋放池中,并會(huì)在當(dāng)前的 runloop 迭代結(jié)束時(shí)釋放。(runloop循環(huán)系統(tǒng)會(huì)隱式創(chuàng)建一個(gè)新的autoreleasepool并在循環(huán)結(jié)束后釋放)
一個(gè)線程創(chuàng)建的時(shí)候就會(huì)有一個(gè)autorelease pool的創(chuàng)建,并且在線程退出的時(shí)候,清空整個(gè)autorelease pool子線程中的autorelease變量
三、子線程中的autorelease變量什么時(shí)候釋放?子線程里面,需要加autoreleasepool嗎?
在子線程創(chuàng)建了 autoreleasepool 的話,產(chǎn)生的 autorelease 對(duì)象就會(huì)交給 autoreleasepool 去管理,在線程退出的時(shí)候,清空整個(gè)autoreleasepool。
如果沒有創(chuàng)建 autoreleasepool ,但是產(chǎn)生了 autorelease 對(duì)象,就會(huì)調(diào)用AutoreleasePoolPage 的 autoreleaseNoPage 方法。該方法會(huì)自動(dòng)創(chuàng)建一個(gè) hotpage,并調(diào)用page->add(obj)將對(duì)象添加到 AutoreleasePoolPage 的棧中。
要弄清線程-runloop-autoreleasepool之間的關(guān)系。
主線程的runloop是默認(rèn)創(chuàng)建的,系統(tǒng)會(huì)監(jiān)聽runloop兩種事件(自動(dòng)釋放池的創(chuàng)建和釋放,銷毀的時(shí)機(jī)):
(1)kCFRunLoopEntry,即將進(jìn)入runloop時(shí),會(huì)創(chuàng)建一個(gè)autoreleasepool。
(2)kCFRunLoopBeforeWaiting,runloop即將休眠時(shí),會(huì)釋放autoreleasepool并創(chuàng)建一個(gè)新的autoreleasepool;
(3)kCFRunLoopExit,runloop即將退出時(shí),會(huì)釋放autoreleasepool。
(4)而autoreleasepool在釋放時(shí),會(huì)對(duì)插入到pool中的對(duì)象發(fā)送release消息。
所以,runloop每次迭代結(jié)束,autoreleasepool釋放,aurelease對(duì)象釋放。
runloop只可以獲取,不可以手動(dòng)創(chuàng)建。
子線程的runloop是手動(dòng)獲取的,在獲取的時(shí)候系統(tǒng)會(huì)創(chuàng)建一個(gè)runloop并返回,所以手動(dòng)獲取到的runloop其實(shí)是系統(tǒng)剛創(chuàng)建好的。
子線程的autoreleasepool也需要手動(dòng)獲取,但區(qū)分情況,一般系統(tǒng)提供的block如usingBlock和GCD提供的block內(nèi)部都會(huì)自動(dòng)包裹一個(gè)autoreleasepool,不用手動(dòng)加。但是你自己通過其他方式創(chuàng)建的子線程,在線程內(nèi)部需要手動(dòng)獲取autoreleasepool,防止局部?jī)?nèi)存使用峰值過高或發(fā)生其他內(nèi)存問題,最后,autoreleasepool釋放時(shí),也會(huì)對(duì)其管理的對(duì)象發(fā)送release消息。
四、那些對(duì)象會(huì)放入自動(dòng)釋放池?
在MRC中,調(diào)用 autorelease 的對(duì)象會(huì)被添加到自動(dòng)釋放池。
在ARC中,以類方法獲取的對(duì)象(注意不是使用alloc/new/copy/mutablCopy獲取的對(duì)象)不會(huì)馬上被銷毀,而是要等到超過autoreleasepool的作用域才會(huì)真實(shí)執(zhí)行release,這是因?yàn)轭惙椒ɡ锏木幾g器幫你返回了一個(gè)autoreleasing的對(duì)象。
Animal *animal1 = [[Animal alloc] init];
Animal *animal2 = [Animal animal];
_objc_autoreleasePoolPrint();
如上代碼打印,alloc創(chuàng)建的animal1并不會(huì)加入自動(dòng)釋放池,類方法創(chuàng)建的 animal2 會(huì)打印
objc[19507]: ##############
objc[19507]: AUTORELEASE POOLS for thread 0x1056cee00
objc[19507]: 1 releases pending.
objc[19507]: [0x7ff56080c000] ................ PAGE (hot) (cold)
objc[19507]: [0x7ff56080c038] 0x6000026e40f0 Animal
objc[19507]: ##############