前言:
Autorelease機(jī)制對(duì)于iOS開(kāi)發(fā)人員對(duì)對(duì)象的內(nèi)存管理省下不少心血,說(shuō)白了就是你甭管內(nèi)存的管理問(wèn)題次绘,我會(huì)在背后幫你處理,不需要你操碎了心去避雷,這就是ARC的最大的好處未妹!正所謂任何事情都是一把雙刃劍,還有一個(gè)block里面循環(huán)引用的雷池要注意的空入,如何注意其實(shí)很簡(jiǎn)單络它,在這就不說(shuō)了。
runloop:
runloop是什么的東西歪赢,runloop就好比一個(gè)特務(wù)化戳,接到任務(wù)就去處理,沒(méi)任務(wù)就休假埋凯,等待任務(wù)点楼,任務(wù)有很多類型的。相對(duì)于線程白对,做完一個(gè)任務(wù)就掛掉了掠廓,runloop還是有很大的優(yōu)勢(shì)的,先看看runloop的大概流程圖:
上圖的2-9流程就是這個(gè)”特務(wù)“的處理任務(wù)甩恼,休假蟀瞧,接到任務(wù)作處理,處理完后再休假(等待任務(wù))的一個(gè)循環(huán)悦污,我說(shuō)了這么多,跟標(biāo)題的內(nèi)容有什么關(guān)系呢屈溉?關(guān)系是有的塞关,本篇重點(diǎn)就在第6點(diǎn)里面了。
圖中第1條的 Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop)子巾,其回調(diào)內(nèi)會(huì)調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動(dòng)釋放池帆赢。其 order 是-2147483647,優(yōu)先級(jí)最高线梗,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前椰于。
這個(gè)用我的話稱為“外圍釋放池”的創(chuàng)建!
圖中第6 Observer 監(jiān)視了兩個(gè)事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池仪搔;Exit(即將退出Loop) 時(shí)調(diào)用 _objc_autoreleasePoolPop() 來(lái)釋放自動(dòng)釋放池瘾婿。這個(gè) Observer 的 order 是 2147483647,優(yōu)先級(jí)最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后偏陪。
圖中第10 Observer 監(jiān)視事件是exit(即講退出runloop)抢呆,其回調(diào)內(nèi)會(huì)調(diào)用 _objc_autoreleasePoolpop() 釋放自動(dòng)釋放池。
這個(gè)用我的話稱為“外圍析放池”的釋放笛谦!
那么第6步的優(yōu)先級(jí)低的釋放池?zé)o論被創(chuàng)建和釋放多少次也對(duì)“外圍釋放池”沒(méi)影響了抱虐,也符合了這個(gè)邏輯思維了!
好了饥脑,上面的內(nèi)容是對(duì)標(biāo)題的內(nèi)容作一個(gè)鋪墊恳邀,那么我今天要深入說(shuō)的就是第6步監(jiān)聽(tīng)的回調(diào)的實(shí)現(xiàn)原理,也就是Autorelease的原理
各位灶轰,先想想這個(gè)“Autorelease對(duì)象什么時(shí)候被切底釋放谣沸?”
答:當(dāng)前作用域大括號(hào)結(jié)束時(shí)釋放。答案真的是這樣嗎笋颤?也許手動(dòng)添加Autorelease pool是這樣的乳附!
真正的答案是:在非手動(dòng)添加Autorelease pool下,Autorelease對(duì)象是在當(dāng)前runloop進(jìn)入休眠等待前被釋放的椰弊,為何會(huì)這樣许溅,接下來(lái)一一探究瓤鼻。
Autorelease內(nèi)部原理
Autoreleasepool{} 編譯后是這樣的:
void * context = objc_autoreleasePoolPush();
{}// 內(nèi)部要做的事情
objc_autoreleasePoolPush(context);
這兩個(gè)方法是對(duì)AutoreleasePoolPage的一個(gè)簡(jiǎn)單封裝秉版,其核心就在這個(gè)類里面,AutoreleasePoolPage是一個(gè)C++實(shí)現(xiàn)的類茬祷;看看里面的內(nèi)容
1.AutoreleasePool并沒(méi)有單獨(dú)的結(jié)構(gòu)清焕,而是由若干個(gè)AutoreleasePoolPage以雙向鏈表的形式組合而成(分別對(duì)應(yīng)結(jié)構(gòu)中的parent指針和child指針)。
2.AutoreleasePool是按線程一一對(duì)應(yīng)的(結(jié)構(gòu)中的thread指針指向當(dāng)前線程)祭犯。
3.AutoreleasePoolPage每個(gè)對(duì)象會(huì)開(kāi)辟4096字節(jié)內(nèi)存(也就是虛擬內(nèi)存一頁(yè)的大薪胀住),除了上面的實(shí)例變量所占空間沃粗,剩下的空間全部用來(lái)儲(chǔ)存autorelease對(duì)象的地址粥惧。
4.上面的id *next指針作為游標(biāo)指向棧頂最新注冊(cè)進(jìn)來(lái)的autorelease對(duì)象的下一個(gè)位置。
5.一個(gè)AutoreleasePoolPage的空間被占滿時(shí)最盅,會(huì)新建一個(gè)AutoreleasePoolPage對(duì)象突雪,連接鏈表(用結(jié)構(gòu)中的parent指針和child指針?lè)謩e指向舊的AutoreleasePoolPage和新的AutoreleasePoolPage),后來(lái)的autorelease對(duì)象在新的page加入涡贱。
當(dāng)我們創(chuàng)建好注冊(cè)進(jìn)來(lái)的對(duì)象就變成這樣
當(dāng)next 指針在棧頂?shù)牡鸵欢葍?nèi)存時(shí)咏删,當(dāng)再注冊(cè)一個(gè)對(duì)象進(jìn)來(lái)時(shí),那么系統(tǒng)就會(huì)創(chuàng)建一個(gè)新的AutoreleasePoolPage來(lái)保存對(duì)象问词,新的parent指針指向舊的AutoreleasePoolPage督函,形成鏈表的結(jié)構(gòu)一樣,保證釋放和新建AutoreleasePool能有序進(jìn)行。每向一個(gè)對(duì)象發(fā)送 autorelease 消息辰狡,就是把對(duì)象加入到next 指針處锋叨,next 指針向高內(nèi)存偏移一個(gè)位置(位置大小取決于新添加進(jìn)來(lái)的對(duì)象內(nèi)存的大小) 這是自動(dòng)釋放對(duì)象add進(jìn)來(lái)的大概原理宛篇。
釋放原理
好了悲柱,自動(dòng)釋放對(duì)象加進(jìn)來(lái)的大概原理我們清楚了,那么反過(guò)來(lái)些己,對(duì)象釋放的原理也差不多了豌鸡。。段标。 慢著涯冠,釋放時(shí)究竟我要釋放到那個(gè)位置呢?憑借什么去標(biāo)記呢逼庞?
內(nèi)涵在于每當(dāng)objc_autoreleasePoolPush被執(zhí)行時(shí)蛇更,runtime會(huì)向當(dāng)前AutoreleasePoolPage加入一個(gè)標(biāo)記,稱為“哨兵指針”赛糟,(我很喜歡這個(gè)名字)這個(gè)哨兵指針內(nèi)容是nil派任,看圖:
AutoreleasePoolPop主要做一下事情:
1.AutoreleasePoolPop要傳入的參數(shù)就是這個(gè)哨兵指針,首先找到這個(gè)哨兵指針?biāo)诘膒age璧南。
2.在next指針和哨兵指針之間的對(duì)象都發(fā)送一次 release消息掌逛,并把next指針移動(dòng)上一次設(shè)立哨兵指針的位置,(由于page結(jié)構(gòu)類似于鏈表司倚,所有這一步可以靠parent指針跨越多個(gè)page的對(duì)象作處理)
以上就是Autorelease內(nèi)部的實(shí)現(xiàn)原理豆混。
我形象點(diǎn)說(shuō)就是page就是一個(gè)氣泵,next指針就是那個(gè)活動(dòng)的活塞动知,哨兵指針地址就是那個(gè)頂點(diǎn)皿伺,當(dāng)我釋放對(duì)象時(shí)就等于我往里面壓縮,把活動(dòng)的活塞推到頂點(diǎn)盒粮,里面的空氣就是add進(jìn)來(lái)的對(duì)象鸵鸥,經(jīng)過(guò)活動(dòng)的活塞的頂點(diǎn)(next指針)向活塞推到頂點(diǎn)(哨兵指針位置)靠近時(shí),空氣(對(duì)象被釋放)被排出丹皱,當(dāng)活動(dòng)的活塞(next指針)被退回到活塞頂點(diǎn)(哨兵指針位置)妒穴,兩者之間的空氣被排出去了(對(duì)象被釋放了)這時(shí)候就不在推了,這時(shí)候當(dāng)再有對(duì)象add進(jìn)來(lái)時(shí)就像有空氣吹進(jìn)來(lái)种呐,把活動(dòng)的活塞的頂點(diǎn)(next指針)和活塞推到頂點(diǎn)(哨兵指針位置)一點(diǎn)一點(diǎn)隔開(kāi)宰翅,一到AutoreleasePoolPop時(shí)又以“推活塞“的形式去釋放對(duì)象了。
好了爽室,總結(jié)上下問(wèn)所說(shuō)的內(nèi)容:當(dāng)一個(gè)runloop在不停的循環(huán)工作汁讼,那么runloop每一次循環(huán)必定會(huì)經(jīng)過(guò)BeforeWaiting(準(zhǔn)備進(jìn)入休眠):而去BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池淆攻,那么這兩個(gè)方法來(lái)銷毀要釋放的對(duì)象,所以我們根本不需要擔(dān)心Autorelease的內(nèi)存管理問(wèn)題嘿架,這就是ARC背后的“高人”瓶珊。
最后,文章一些內(nèi)容引用到一些大神級(jí)別的博客的內(nèi)容耸彪,如有侵權(quán)伞芹,請(qǐng)及時(shí)聯(lián)系。
由于水平有限蝉娜,僅供大家學(xué)習(xí) (大神請(qǐng)無(wú)視)如有錯(cuò)誤唱较,請(qǐng)指正!
最后的最后貼上一些更深入了解的博客地址: