AutoreleasePool 是一個(gè)抽象概念,并沒有實(shí)際結(jié)構(gòu),真實(shí)的結(jié)構(gòu)是一個(gè)雙向鏈表『AutoreleasePoolPage』,由C++實(shí)現(xiàn)。
1.數(shù)據(jù)結(jié)構(gòu)
其數(shù)據(jù)結(jié)構(gòu)如下:
class AutoreleasePoolPage
{
#define POOL_SENTINEL nil
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MAX_SIZE; // size and alignment, power of 2
#endif
static size_t const COUNT = SIZE / sizeof(id);
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
}
成員變量說明:
next:游標(biāo),一直指向最新入棧的autorelease對象的下一個(gè)位置。
key:TLS技術(shù),既Thread Local Storage(TLS)線程局部存儲。目的很簡單,將一塊內(nèi)存作為某個(gè)線程專有的存儲,以key-value的形式進(jìn)行讀寫。AutoreleasePoolPage將這塊區(qū)域用作存儲最新的Page,既hotPage。
SIZE:單個(gè)page的最大存儲數(shù)量,AutoreleasePoolPage以雙向鏈表的形式存在,但是單個(gè)page的存儲是有限額的,(id *) ((uint8_t *)this+SIZE)。
thread:當(dāng)前線程pthread_self()。
2.push和pop
當(dāng)我們手動調(diào)用@autoreleasePool的時(shí)候,編譯器會自動將大括號內(nèi)的所有對象標(biāo)記為autorelease前綴。AutoreleasePoolPage::autorelease((id)this)當(dāng)前對象本身作為參數(shù)入?yún)?,不過在討論對象的autorelease之前,編譯器還插入了兩個(gè)方法:
void *pool = objc_autoreleasePoolPush();
……
……
……
objc_autoreleasePoolPop(pool);
前者做了和對象的autorelease相同的事情:
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);
}
}
id *add(id obj)
{
assert(!full());
unprotect();
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;
protect();
return ret;
}
主要就是移動游標(biāo),并且返回當(dāng)前位置,這個(gè)返回參數(shù)的意義主要體現(xiàn)在
objc_autoreleasePoolPop(pool);的入?yún)⒅?,pool記錄著一個(gè)pool的初始位置,根據(jù)這個(gè)位置和當(dāng)前的hotPage位置,遍歷中間所有的對象,進(jìn)行釋放。并且由于雙向鏈表的結(jié)構(gòu),很容易跨page進(jìn)行遍歷。