因?yàn)楝F(xiàn)在大家都在使用ARC模式下進(jìn)行編程,一個(gè)很重要的問題也是最容易被大家所忽視的問題就是自動(dòng)釋放池,大部分程序員尤其是剛?cè)胄械亩贾皇侵烙羞@么一個(gè)東西,但具體是什么,工作的原理是什么,在什么時(shí)候使用它都一概不知。所以寫一篇文章,記錄一下個(gè)人對(duì)自動(dòng)釋放池的一些理解。
我們新建一個(gè)OC項(xiàng)目,在main函數(shù)中可以看到這么一串代碼:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
其中就引用了一個(gè)自動(dòng)釋放池。其實(shí),自動(dòng)釋放池@autoreleasepool{}在系統(tǒng)中所編譯的代碼為:
1.void *ctx = objc_autoreleasepoolPush(); ? ? //創(chuàng)建一個(gè)無類型指針的哨兵對(duì)象
2.執(zhí)行@autoreleasepool{}中對(duì)應(yīng){}里所書寫的代碼
3.執(zhí)行objc_autoreleasepool(ctx);//釋放哨兵對(duì)象所分隔區(qū)域內(nèi)所有對(duì)象的引用計(jì)數(shù)
由以上可以看出,autoreleasepool工作的原理就是一個(gè)壓棧,一個(gè)出棧,push方法創(chuàng)建哨兵對(duì)象作為標(biāo)記,pop操作作批量引用計(jì)數(shù)釋放的工作。
介紹完大概代碼的原理之后說一下一個(gè)比較官方的對(duì)于autoreleasePool的總結(jié)吧。
autoreleasepool是以棧為節(jié)點(diǎn),通過雙向鏈表的形式組合而成的,autoreleasepool是與線程一一對(duì)應(yīng)的。
那么什么是雙向鏈表呢,雙向鏈表的單位是節(jié)點(diǎn),從頭結(jié)點(diǎn)開始一直到尾節(jié)點(diǎn),每一個(gè)節(jié)點(diǎn)都會(huì)有一個(gè)父指針和子指針,父指針指向前一個(gè)節(jié)點(diǎn),子指針指向后一個(gè)節(jié)點(diǎn),而頭節(jié)點(diǎn)的父指針指向NULL,尾節(jié)點(diǎn)的子指針指向NULL。這樣就把??臻g通過雙向鏈表的形式連接起來了。
而autoreleasepoolPage就是雙鏈鏈表中每一個(gè)節(jié)點(diǎn)的長(zhǎng)度了,可以說,autoreleasepoolPage把??臻g中一些大小的空間分割成一個(gè)個(gè)單位,稱為節(jié)點(diǎn),然后通過雙向鏈表的形式連接起來,這就成了一個(gè)整體的autoreleasepool的結(jié)構(gòu)了。
在AutoreleasePool中有四個(gè)變量分別是:1.id *next一個(gè)id類型的指針,指向的是下一個(gè)可存儲(chǔ)對(duì)象的位置,2.AutoreleasepoolPage* const parent;這就是當(dāng)前page父節(jié)點(diǎn)page的地址指針。3.AutoreleasepoolPage* child,同理,是子節(jié)點(diǎn)表地址的指針。4.pthread_t const thread;這個(gè)變量中就記錄了線程的情況,所以說自動(dòng)釋放池是與線程一一對(duì)應(yīng)的關(guān)系。
在介紹了自動(dòng)釋放池整體結(jié)構(gòu)之后,接下來就說自動(dòng)釋放池關(guān)大象的三個(gè)步驟:
一、把冰箱門打開
調(diào)用objc_autoreleasepoolPush()方法,在當(dāng)前autoreleasepoolPage中的next指針位置創(chuàng)建一個(gè)為nil的哨兵對(duì)象,隨后將next指針的位置指向下一個(gè)內(nèi)存地址。
二、把大象關(guān)進(jìn)去
第二步就是執(zhí)行代碼了,在自動(dòng)釋放池范圍內(nèi)的代碼被執(zhí)行,給逐個(gè)對(duì)象調(diào)用[object autorelease]方法。其實(shí)在autorelease方法內(nèi)部實(shí)現(xiàn)的步驟為:1.判斷next指針是否已經(jīng)在棧頂了,如果是,則增加一個(gè)棧節(jié)點(diǎn)到鏈表上,隨后增加一個(gè)對(duì)象到新的棧節(jié)點(diǎn)鏈表中,如果不是的話則在next指針?biāo)傅奈恢锰砑右粋€(gè)調(diào)用autorelease方法的對(duì)象。
三、把冰箱門關(guān)上(其實(shí)是把大象取出來)
第三部就是執(zhí)行objc_autoreleasepoolPop()方法,該方法會(huì)根據(jù)傳入的哨兵對(duì)象找到對(duì)應(yīng)的內(nèi)存位置,然后根據(jù)哨兵對(duì)象的位置給上次push后添加的對(duì)象依次發(fā)送release消息,然后回退next指針到正確的位置。
以上三個(gè)步驟就是在autorelease自動(dòng)釋放池中進(jìn)行的操作,也是這三個(gè)步驟構(gòu)成了自動(dòng)釋放池。
所以總結(jié)一下,main函數(shù)中的autoreleasepool是在runloop結(jié)束的時(shí)候調(diào)用objc_autoreleasepoolPop的方法的,多層嵌套的autoreleasepool其實(shí)就是在棧中多次插入哨兵對(duì)象,而在我們開發(fā)的過程中,通過for循環(huán)加載一些占用內(nèi)存較大的對(duì)象時(shí)可以嵌套使用autoreleasepool,在這些對(duì)象使用完畢的時(shí)候及時(shí)被釋放掉,這樣就不會(huì)造成內(nèi)存過大或過多浪費(fèi)的情況啦~
本文章由作者原創(chuàng),未經(jīng)允許不得轉(zhuǎn)載!