博客連接AutoreleasePool的實現編程
在MRC中,調用[obj autorelease]
來延遲內存的釋放;在ARC下,對象調用autorelease
方法,就會被自動添加到最近的自動釋放池,只有當自動釋放池被銷燬的時候,纔會執行release
方法,進行釋放。真實結果究竟是什麼,等看完源碼後咱們就會知道了。緩存
main.m
中有一段代碼:bash
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
複製代碼
轉換成C++代碼:app
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
}
}
複製代碼
@autoreleasepool
變成__AtAutoreleasePool __autoreleasepool
。__AtAutoreleasePool
結構體定義以下:less
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
複製代碼
它提供了兩個方法:objc_autoreleasePoolPush()
和objc_autoreleasePoolPop
。這兩個方法的定義在NSObject.mm
文件中,分別是:函數
void *
objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
複製代碼
因此,autoreleasepool的自動釋放的核心就是AutoreleasePoolPage類。oop
在NSObject.mm
文件中定義了AutoreleasePoolPage
,這裏咱們只顯示這個類比較重要的屬性,以下:測試
class AutoreleasePoolPage {
static size_t const SIZE = PAGE_MAX_SIZE;//SIZE是AutoreleasePoolPage的大小,4096個字節
magic_t const magic; //autoreleasepool完整性校驗
id *next;//AutoreleasePoolPage單個節點是一個鏈表,next指向棧頂的最新的autorelease對象的下一個位置
pthread_t const thread;//當前所在的線程
AutoreleasePoolPage * const parent;//指針
AutoreleasePoolPage *child;//指針
uint32_t const depth;//深度
uint32_t hiwat;
}
複製代碼
經過源碼咱們能夠知道:優化
AutoreleasePoolPage
爲節點的雙向鏈表。AutoreleasePoolPage
節點是一個堆棧結,且大小爲4096個字節。AutoreleasePoolPage
節點對應着一個線程,屬於一一對應關係。AutoreleasePool結構如圖所示:ui
接着咱們看一下AutoreleasePoolPage
的構造函數以及一些操做方法:
//構造函數
AutoreleasePoolPage(AutoreleasePoolPage *newParent)
: magic(), next(begin()), thread(pthread_self()),
parent(newParent), child(nil),
depth(parent ? 1+parent->depth : 0),
hiwat(parent ? parent->hiwat : 0)
{
if (parent) {
parent->check();
assert(!parent->child);
parent->unprotect();
parent->child = this;
parent->protect();
}
protect();
}
//相關操做方法
id * begin() {
return (id *) ((uint8_t *)this+sizeof(*this));
}
id * end() {
return (id *) ((uint8_t *)this+SIZE);
}
bool empty() {
return next == begin();
}
bool full() {
return next == end();
}
bool lessThanHalfFull() {
return (next - begin() < (end() - begin()) / 2);
}
id *add(id obj)
{
assert(!full());
unprotect();
id *ret = next; // faster than `return next-1` because of aliasing
*next++ = obj;
protect();
return ret;
}
複製代碼
AutoreleasePoolPage
節點開始存autorelease對象的位置。AutoreleasePoolPage
節點最大的位置next
指向beigin()說明爲空next
指向end)說明滿了因此一個空的AutoreleasePoolPage
的結構以下:
push代碼以下:
static inline void *push()
{
id *dest;
if (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執行的時候首先會進行判斷,若是是須要每一個pool都生成一個新page,即DebugPoolAllocation
爲YES
,則執行autoreleaseNewPage
方法,不然執行autoreleaseFast
方法。
autoreleaseNewPage
分爲兩種狀況:
autoreleaseFullPage
方法;autoreleaseNoPage
方法。autoreleaseFast
分爲三種狀況:
add()
方法進行添加;autoreleaseFullPage
方法;autoreleaseNoPage
方法。前面講到的page其實就是hotPage
,經過AutoreleasePoolPage *page = hotPage();
獲取。
static inline AutoreleasePoolPage *hotPage()
{
AutoreleasePoolPage *result = (AutoreleasePoolPage *)
tls_get_direct(key);
if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
if (result) result->fastcheck();
return result;
}
複製代碼
經過上面的代碼咱們知道當前頁是存在TLS(線程私有數據)
裏面的。因此說第一次調用push的時候,沒有page天然連hotPage也沒有。
static __attribute__((noinline))
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
會從傳入的page
開始遍歷整個雙向鏈表,若是page
滿了,就看它的child
節點,直到查找到一個未滿的AutoreleasePoolPage
。接着使用AutoreleasePoolPage
構造函數傳入parent
建立一個新的AutoreleasePoolPage
的節點(此時跳出了while循環)。
在查找到一個可使用的AutoreleasePoolPage
以後,會將該頁面標記成hotPage
,而後調動add()
方法添加對象。
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
//"no page"意味着沒有沒有池子被push或者說push了一個空的池子
assert(!hotPage());
bool pushExtraBoundary = false;
if (haveEmptyPoolPlaceholder()) {//push了一個空的池子
pushExtraBoundary = true;
}
else if (obj != POOL_BOUNDARY && DebugMissingPools) {
_objc_inform("MISSING POOLS: (%p) Object %p of class %s "
"autoreleased with no pool in place - "
"just leaking - break on "
"objc_autoreleaseNoPool() to debug",
pthread_self(), (void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}
else if (obj == POOL_BOUNDARY && !DebugPoolAllocation) {
//沒有池子被push
return setEmptyPoolPlaceholder();
}
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
if (pushExtraBoundary) {
//push了一個空的池子,添加哨兵對象
page->add(POOL_BOUNDARY);
}
return page->add(obj);
}
//haveEmptyPoolPlaceholder的本質
static inline bool haveEmptyPoolPlaceholder()
{
id *tls = (id *)tls_get_direct(key);
return (tls == EMPTY_POOL_PLACEHOLDER);
}
複製代碼
從上面的代碼咱們能夠知道,既然當前內存中不存在AutoreleasePoolPage
,就要從頭開始構建這個自動釋放池的雙向鏈表,也就是說,新的AutoreleasePoolPage
是沒有parent
指針的。
初始化以後,將當前頁標記爲hotPage
,而後會先向這個page
中添加一個POOL_BOUNDARY
的標記,來確保在pop
調用的時候,不會出現異常。
最後,將obj
添加到自動釋放池中。
接着看一下當對象調用autorelase
方法發生了什麼。
- (id)autorelease {
return ((id)self)->rootAutorelease();
}
inline id
objc_object::rootAutorelease()
{
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
return rootAutorelease2();
}
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
assert(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
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;
}
複製代碼
從上面的源碼咱們看到,對象調用autorelase
方法,最後會變成AutoreleasePoolPage
的autorelease
函數。AutoreleasePoolPage
的autorelease
的本質就是調用autoreleaseFast(obj)
函數。只不過push
操做插入的是一個POOL_BOUNDARY
,而autorelease
操做插入的是一個具體的autoreleased
對象即AutoreleasePoolPage
入棧操做。
固然這麼說並不嚴謹,由於咱們須要考慮是不是Tagged Pointer
和是否進行優化的狀況(prepareOptimizedReturn
這個後面也會提到),若是不知足這兩個條件纔會進入緩存池。
因此push的流程是:
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
//第一種狀況:autoreleasepool首次push的時候返回的,也就是最頂層的page執行pop會執行這一部分
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// Popping the top-level placeholder pool.
if (hotPage()) {
// Pool was used. Pop its contents normally.
// Pool pages remain allocated for re-use as usual.
pop(coldPage()->begin());
} else {
// Pool was never used. Clear the placeholder.
setHotPage(nil);
}
return;
}
page = pageForPointer(token);
//https://stackoverflow.com/questions/24952549/does-nsthread-create-autoreleasepool-automatically-now
//第二種狀況:在非ARC的狀況下,在新建立的線程中不使用autoreleasepool,直接調用autorelease方法時會出現這個狀況。此時沒有pool,直接進行autorelease。
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 (PrintPoolHiwat) printHiwat();
//第三種狀況:也就是咱們常常碰到的狀況
page->releaseUntil(stop);
// memory: delete empty children
if (DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (DebugMissingPools && page->empty() && !page->parent) {
// special case: delete everything for pop(top)
// when debugging missing autorelease pools
page->kill();
setHotPage(nil);
}
else 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();
}
}
}
複製代碼
這裏咱們主要分析下第三種狀況。
void releaseUntil(id *stop) {
while (this->next != stop) {
AutoreleasePoolPage *page = hotPage();
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);
}
複製代碼
從next指針開始,一個一個向前調用objc_release
,直到碰到push時壓入的pool爲止。
因此autoreleasePool的運行過程應該是:
pool1 = push()
...
pool2 = push()
...
pool3 = push()
...
pop(pool3)
...
pop(pool2)
...
pop(pool1)
複製代碼
每次pop,實際上都會把最近一次push以後添加進去的對象所有release掉。
蘋果的文檔中提到:
Each NSThread object, including the application’s main thread, has an NSRunLoop object automatically created for it as needed.
The Application Kit creates an autorelease pool on the main thread at the beginning of every cycle of the event loop, and drains it at the end, thereby releasing any autoreleased objects generated while processing an event.
Each thread (including the main thread) maintains its own stack of NSAutoreleasePool objects.
咱們能夠知道:
每個線程,包括主線程,都會擁有一個專屬的runloop,而且會在有須要的時候自動建立。
主線程在runloop開始以前會自動建立一個autoreleasePool,並在結束時pop。那其餘的線程呢?這裏我作了一個實驗,代碼以下:
每個線程都會維護本身的autoreleasePool堆棧,也就是說每個autoreleasePool對應一個線程。
那什麼樣的對象會進入autoreleasePool呢?
測試1.1代碼:
NSMutableArray *arr = [NSMutableArray new];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
複製代碼
結果
2019-01-22 15:50:45.129263+0800 AutoreleasePool[31529:22121744] 1
objc[31529]: ##############
objc[31529]: AUTORELEASE POOLS for thread 0x1176345c0
objc[31529]: 2 releases pending.
objc[31529]: [0x7f96e0802000] ................ PAGE (hot) (cold)
objc[31529]: [0x7f96e0802038] 0x600003ac8f00 __NSArrayI
objc[31529]: [0x7f96e0802040] 0x600000ceef80 __NSSetI
objc[31529]: ##############
複製代碼
測試1.2代碼:
@autoreleasepool{
NSMutableArray *arr = [NSMutableArray new];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
複製代碼
結果
2019-01-22 15:53:28.818873+0800 AutoreleasePool[31568:22134125] 1
objc[31568]: ##############
objc[31568]: AUTORELEASE POOLS for thread 0x10d20e5c0
objc[31568]: 3 releases pending.
objc[31568]: [0x7fcf66002000] ................ PAGE (hot) (cold)
objc[31568]: [0x7fcf66002038] 0x600000129200 __NSArrayI
objc[31568]: [0x7fcf66002040] 0x60000370b020 __NSSetI
objc[31568]: [0x7fcf66002048] ################ POOL 0x7fcf66002048
objc[31568]: ##############
複製代碼
測試1.3代碼:
{
NSMutableArray *arr = [NSMutableArray new];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
@autoreleasepool{
NSMutableArray *arr = [NSMutableArray new];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
複製代碼
結果
2019-01-22 15:55:21.271452+0800 AutoreleasePool[31596:22141965] 1
objc[31596]: ##############
objc[31596]: AUTORELEASE POOLS for thread 0x1166f15c0
objc[31596]: 2 releases pending.
objc[31596]: [0x7fdcaf002000] ................ PAGE (hot) (cold)
objc[31596]: [0x7fdcaf002038] 0x600003e6a500 __NSArrayI
objc[31596]: [0x7fdcaf002040] 0x600000849db0 __NSSetI
objc[31596]: ##############
2019-01-22 15:55:21.272353+0800 AutoreleasePool[31596:22141965] 1
objc[31596]: ##############
objc[31596]: AUTORELEASE POOLS for thread 0x1166f15c0
objc[31596]: 3 releases pending.
objc[31596]: [0x7fdcaf002000] ................ PAGE (hot) (cold)
objc[31596]: [0x7fdcaf002038] 0x600003e6a500 __NSArrayI
objc[31596]: [0x7fdcaf002040] 0x600000849db0 __NSSetI
objc[31596]: [0x7fdcaf002048] ################ POOL 0x7fdcaf002048
objc[31596]: ##############
複製代碼
測試2.1代碼:
NSMutableArray *arr = [NSMutableArray array];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
複製代碼
結果
2019-01-22 15:57:02.360860+0800 AutoreleasePool[31615:22149043] 2
objc[31615]: ##############
objc[31615]: AUTORELEASE POOLS for thread 0x1111eb5c0
objc[31615]: 3 releases pending.
objc[31615]: [0x7fbf00002000] ................ PAGE (hot) (cold)
objc[31615]: [0x7fbf00002038] 0x600003f15c00 __NSArrayI
objc[31615]: [0x7fbf00002040] 0x6000009705f0 __NSSetI
objc[31615]: [0x7fbf00002048] 0x600002404f30 __NSArrayM
objc[31615]: ##############
複製代碼
測試2.2代碼:
@autoreleasepool {
NSMutableArray *arr = [NSMutableArray array];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
複製代碼
結果
2019-01-22 15:58:29.932693+0800 AutoreleasePool[31634:22153810] 2
objc[31634]: ##############
objc[31634]: AUTORELEASE POOLS for thread 0x115aac5c0
objc[31634]: 4 releases pending.
objc[31634]: [0x7f867c002000] ................ PAGE (hot) (cold)
objc[31634]: [0x7f867c002038] 0x600000b00080 __NSArrayI
objc[31634]: [0x7f867c002040] 0x600003d64190 __NSSetI
objc[31634]: [0x7f867c002048] ################ POOL 0x7f867c002048
objc[31634]: [0x7f867c002050] 0x60000100ff30 __NSArrayM
objc[31634]: ##############
複製代碼
測試2.3代碼:
{
NSMutableArray *arr = [NSMutableArray array];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
@autoreleasepool {
NSMutableArray *arr = [NSMutableArray array];
NSLog(@"%lu", _objc_rootRetainCount(arr));
_objc_autoreleasePoolPrint();
}
複製代碼
結果
2019-01-22 16:01:11.925690+0800 AutoreleasePool[31670:22164284] 2
objc[31670]: ##############
objc[31670]: AUTORELEASE POOLS for thread 0x11bffa5c0
objc[31670]: 3 releases pending.
objc[31670]: [0x7ff965802000] ................ PAGE (hot) (cold)
objc[31670]: [0x7ff965802038] 0x600001c1eb00 __NSArrayI
objc[31670]: [0x7ff965802040] 0x600002a47200 __NSSetI
objc[31670]: [0x7ff965802048] 0x600000712490 __NSArrayM
objc[31670]: ##############
2019-01-22 16:01:11.926577+0800 AutoreleasePool[31670:22164284] 1
objc[31670]: ##############
objc[31670]: AUTORELEASE POOLS for thread 0x11bffa5c0
objc[31670]: 4 releases pending.
objc[31670]: [0x7ff965802000] ................ PAGE (hot) (cold)
objc[31670]: [0x7ff965802038] 0x600001c1eb00 __NSArrayI
objc[31670]: [0x7ff965802040] 0x600002a47200 __NSSetI
objc[31670]: [0x7ff965802048] 0x600000712490 __NSArrayM
objc[31670]: [0x7ff965802050] ################ POOL 0x7ff965802050
objc[31670]: ##############****
複製代碼
從上面的代碼咱們能夠知道,使用new
、alloc
這樣的方法建立的對象實例是不會進入autoreleasePool的,可是使用簡便方法建立的對象如[NSMutableArray array]
是會進入自動緩存池的。
可是在測試2.3上,咱們能夠看到,只有一個array進入了自動緩存池,另一個沒有進入。看一下它的方法調用棧:
在《Objective-C高級編程》第66-67頁提到了最優化程序運行。經過objc_retainAutoreleasedReturnValue
和objc_retainAutoreleaseReturnValue
函數的協做,能夠不將對象註冊到autoreleasePool中而直接傳遞,這一過程達到最優化。
objc_retainAutoreleasedReturnValue
的實現以下:
// Accept a value returned through a +0 autoreleasing convention for use at +1.
id
objc_retainAutoreleasedReturnValue(id obj)
{
if (acceptOptimizedReturn() == ReturnAtPlus1) return obj;
return objc_retain(obj);
}
// Try to accept an optimized return.
// Returns the disposition of the returned object (+0 or +1).
// An un-optimized return is +0.
static ALWAYS_INLINE ReturnDisposition
acceptOptimizedReturn()
{
ReturnDisposition disposition = getReturnDisposition();
setReturnDisposition(ReturnAtPlus0); // reset to the unoptimized state
return disposition;
}
static ALWAYS_INLINE ReturnDisposition
getReturnDisposition()
{
return (ReturnDisposition)(uintptr_t)tls_get_direct(RETURN_DISPOSITION_KEY);
}
複製代碼
經過上面的代碼咱們能夠知道objc_retainAutoreleasedReturnValue
會嘗試接收一個被優化的結果,如何是ReturnAtPlus1
即YES
,返回對象自己,不然執行objc_retain(obj
。
這個被優化的結果是在線程私有數據TLS中的,咱們能夠理解爲一個優化位。當優化位返回YES的時候,直接返回對象自己,不然執行retain。
objc_retainAutoreleaseReturnValue
的實現以下:
// Prepare a value at +0 for return through a +0 autoreleasing convention.
id
objc_retainAutoreleaseReturnValue(id obj)
{
if (prepareOptimizedReturn(ReturnAtPlus0)) return obj;
// not objc_autoreleaseReturnValue(objc_retain(obj))
// because we do not need another optimization attempt
return objc_retainAutoreleaseAndReturn(obj);
}
// Try to prepare for optimized return with the given disposition (+0 or +1).
// Returns true if the optimized path is successful.
// Otherwise the return value must be retained and/or autoreleased as usual.
static ALWAYS_INLINE bool
prepareOptimizedReturn(ReturnDisposition disposition)
{
assert(getReturnDisposition() == ReturnAtPlus0);
if (callerAcceptsOptimizedReturn(__builtin_return_address(0))) {
if (disposition) setReturnDisposition(disposition);
return true;
}
return false;
}
static ALWAYS_INLINE bool
callerAcceptsOptimizedReturn(const void * const ra0)
{
const uint8_t *ra1 = (const uint8_t *)ra0;
const unaligned_uint16_t *ra2;
const unaligned_uint32_t *ra4 = (const unaligned_uint32_t *)ra1;
const void **sym;
#define PREFER_GOTPCREL 0
#if PREFER_GOTPCREL
// 48 89 c7 movq %rax,%rdi
// ff 15 callq *symbol@GOTPCREL(%rip)
if (*ra4 != 0xffc78948) {
return false;
}
if (ra1[4] != 0x15) {
return false;
}
ra1 += 3;
#else
// 48 89 c7 movq %rax,%rdi
// e8 callq symbol
if (*ra4 != 0xe8c78948) {
return false;
}
ra1 += (long)*(const unaligned_int32_t *)(ra1 + 4) + 8l;
ra2 = (const unaligned_uint16_t *)ra1;
// ff 25 jmpq *symbol@DYLDMAGIC(%rip)
if (*ra2 != 0x25ff) {
return false;
}
#endif
ra1 += 6l + (long)*(const unaligned_int32_t *)(ra1 + 2);
sym = (const void **)ra1;
if (*sym != objc_retainAutoreleasedReturnValue &&
*sym != objc_unsafeClaimAutoreleasedReturnValue)
{
return false;
}
return true;
}
// Same as objc_retainAutorelease but suitable for tail-calling
// if you do not want to push a frame before this point.
__attribute__((noinline))
static id
objc_retainAutoreleaseAndReturn(id obj)
{
return objc_retainAutorelease(obj);
}
id
objc_retainAutorelease(id obj)
{
return objc_autorelease(objc_retain(obj));
}
複製代碼
這裏主要涉及到callerAcceptsOptimizedReturn
,這個函數意思不是很理解,可是裏面涉及到了objc_retainAutoreleasedReturnValue
,猜想多是程序檢測在返回值以後是否緊接着調用了objc_retainAutoreleasedReturnValue
,若是是,就知道了外部是ARC環境走優化路線,反之就走沒被優化的邏輯。
因此我的認爲,使用new
、alloc
這樣的方法建立的對象實例是不會進入autoreleasePool的,可是使用簡便方法建立的對象,程序會進行優化後,再決定是否進入自動緩存池。
另外關於main函數中的@autoreleasepool
的做用是什麼?簡單的說就是讓那些進入自動緩存池的對象有個地方被釋放。
在APP中,整個主線程是運行在一個自動釋放池中的。
main函數中的自動釋放池的做用:這個池塊給出了一個pop點來顯式的告訴咱們這裏有一個釋放點,若是你的main在初始化的過程當中有別的內容能夠放在這裏。
使用@autoreleasepool
標記,調用push()方法。
沒有hotpage,調用autoreleaseNoPage()
,設置EMPTY_POOL_PLACEHOLDER
。
由於設置了EMPTY_POOL_PLACEHOLDER
,因此會設置本頁爲hotpage
,添加邊界標記POOL_BOUNDARY
,最後添加obj。
繼續有對象調用autorelease
,此時已經有了page,調用page->add(obj)
。
若是page滿了,調用autoreleaseFullPage()
建立新page,重複第6點。
到達autoreleasePool邊界,調用pop方法,一般狀況下會釋放掉POOL_BOUNDARY
以後的全部對象