Autorelease機制講解

Autorelease機制是在iOS內存管理中的一員。在MRC中,是通過調用[obj autorelease]來延遲內存釋放;在ARC中,我們已經完全不需要知道Autorelease就能很好地管理好內存。而在這背後,Objective-C幫我們做了什麼呢,又是如何正確的管理好內存呢,下面我們來講解Autorelease機制,希望大家對Autorelease有所進一步的瞭解!!!

Autorelease對象什麼時候釋放呢?

我不知道大家在面試的時候,有沒有遇到過這樣的問題,本人在悅動天下面試遇到過。如果拿Autorelease對象什麼時候釋放拿來做面試題,可能正確回答上來的沒有幾個,大家可能都會回答,“當前作用域也就是大括號結束時釋放”,如果這樣回答,顯然沒有正確很好地理解Autorelease機制。
歡迎加羣 499 7 54 6 14 學習交流,備註cs

在如果沒有手動加Autorelease Pool情況下,Autorelease對象是在當前runloop迭代結束之後纔會釋放,釋放的原因是因爲系統在每個runloop迭代中都已經加入了自動釋放池push和pop。

(每個runloop都會創建一個autoreleasepool並在runloop迭代結束之後進行釋放)

下面是一段代碼講述ARC與MRC的autorelease的使用,如下:

複製代碼
// MRC
NSAutoreleasePool *pool = [NSAutoreleasePool alloc] init];
id obj = [NSObject alloc] init];
[obj autorelease];
[pool drain];

// ARC
@autoreleasepool {
id obj = [NSObject alloc] init];
}
複製代碼

Autorelease的蘋果實現

我們可以通過Objective-C庫runtime/objc-arr.mm來看一下蘋果Autorelease實現。

複製代碼
class AutoreleasePoolPage
{
static inline void *push()
{
相當於生成或持有NSAutoreleasePool類對象
}
static inline void *pop(void *token)
{
相當於廢棄NSAutoreleasePool類對象
releaseAll();
}
static inline id autorelease(id obj)
{
相當於NSAutoreleasePool類的addObject類方法
AutoreleasePoolPage *autoreleasePoolPage = 取得正在使用的AutoreleasePoolPage實例;
autoreleasePoolPage->add(obj);
}
id *add(id obj)
{
將對象追加到內部數組中
}
void releaseAll()
{
調用內部數組中對象的release實例方法
}
};
void *objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
id *objc_autorelease(id obj)
{
return AutoreleasePoolPage::autorelease(obj);
}
複製代碼
在iOS 程序啓動之後,主線程會創建一個Runloop,也會創建兩個Observer,回調工作都是在_wrapRunLoopWithAutoreleasePoolHandler()函數中。

第一個Observer監聽的是Entry(即將進入Loop),回調是在_objc_autoreleasePoolPush()中創建自動釋放池的,優先級是最高的,保證創建釋放池是在所有回調之前。

第二個Observer監聽有兩個事件:BeforeWaiting(進入休眠)時調用_objc_autoreleasePoolPop()和_objc_autoreleasePoolPush()釋放舊的釋放池以及創建新的釋放池;Exit(退出Loop)調用_objc_autoreleasePoolPop()來釋放自動釋放池。這個優先級是最低的,保證釋放池發生在所有回調之後調用。

通過上面的代碼發現AutoreleasePoolPage是核心類:函數主要是調用了push和pop方法。下面我們一起看一下AutoreleasePoolPage這個類。

AutoreleasePoolPage

下面是AutoreleasePoolPage類:

複製代碼

define EMPTY_POOL_PLACEHOLDER ((id*)1)

define POOL_BOUNDARY 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;

複製代碼
在ARC下,我們如果使用@autoreleasepool{}來創建一個AutoreleasePool,隨後編譯器將會改成下面:

void *context = objc_autoreleasePoolPush();
// {}中的代碼
objc_autoreleasePoolPop(context);
AutoreleasePoolPage是依靠C++實現的類。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章