淺析AutoreleasePool源碼

最近在拜讀Draveness大佬的一篇文章自動(dòng)釋放池的前世今生 ---- 深入解析 autoreleasepool,看到文中給讀者留了一個(gè)問題:

我到現(xiàn)在也不是很清楚為什么要根據(jù)當(dāng)前頁(yè)的不同狀態(tài) kill 掉不同 child 的頁(yè)面岛啸。

關(guān)于AutoreleasePool是什么秋冰,強(qiáng)力推薦閱讀原文讨惩,寫的很好睁壁。這里就不說(shuō)了,直接討論問題歼郭。

首先是整個(gè)pop方法的實(shí)現(xiàn):

    static inline void pop(void *token) 
    {
        AutoreleasePoolPage *page;
        id *stop;

        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);
        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 fully
            if (page->lessThanHalfFull()) {
                page->child->kill();
            }
            else if (page->child->child) {
                page->child->child->kill();
            }
        }
    }

我們先看看釋放的函數(shù)releaseUntil遗契,它在釋放的時(shí)候其實(shí)會(huì)一直順著parent往前釋放,直到參數(shù)stop病曾,也就是說(shuō)可能一次性釋放好幾個(gè)page牍蜂。

// 代碼有所刪減
void releaseUntil(id *stop)
{
    while (this->next != stop) {
        AutoreleasePoolPage *page = hotPage();
        
        while (page->empty()) {
            page = page->parent;
            setHotPage(page);
        }
        
        id obj = *--page->next;
        memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
        
        if (obj != POOL_BOUNDARY) {
            objc_release(obj);
        }
    }
    
    setHotPage(this);
}

然后我們來(lái)看看這段有疑問的代碼

        // memory: delete empty children
        if (DebugPoolAllocation  &&  page->empty()) {  // 分支1
            // special case: delete everything during page-per-pool debugging
            AutoreleasePoolPage *parent = page->parent;
            page->kill();
            setHotPage(parent);
        } else if (DebugMissingPools  &&  page->empty()  &&  !page->parent) { // 分支2
            // special case: delete everything for pop(top) 
            // when debugging missing autorelease pools
            page->kill();
            setHotPage(nil);
        } 
        else if (page->child) { // 分支3
            // hysteresis: keep one empty child if page is more than half fully
            if (page->lessThanHalfFull()) {
                page->child->kill();
            }
            else if (page->child->child) {
                page->child->child->kill();
            }
        }

這塊代碼的作用是刪除空的子節(jié)點(diǎn),釋放內(nèi)存泰涂。pop之后三種情況:

  1. 當(dāng)前page為空鲫竞,直接kill掉當(dāng)前page,然后把parent設(shè)置為hotpage逼蒙;
  2. 當(dāng)前page為空从绘,而且沒有parent,kill掉當(dāng)前page是牢,hotpage置為空僵井;
  3. 當(dāng)前page不為空,但是有child驳棱,如果當(dāng)前page的空間占用不到一半批什,釋放child,如果當(dāng)前page的空間占用超過(guò)一半社搅,且child還有child驻债,直接釋放這個(gè)孫子輩的page。(對(duì)于第三步注釋中的解釋是:keep one empty child if page is more than half fully)

我們?cè)倏纯?code>kill的實(shí)現(xiàn)罚渐,可以發(fā)現(xiàn)他是會(huì)順著child一直往后釋放却汉,保證釋放節(jié)點(diǎn)的child page都被釋放了。

void kill()
{
    AutoreleasePoolPage *page = this;
    while (page->child) page = page->child;
    
    AutoreleasePoolPage *deathptr;
    do {
        deathptr = page;
        page = page->parent;
        if (page) {
            page->unprotect();
            page->child = nil;
            page->protect();
        }
        delete deathptr;
    } while (deathptr != this);
}

到這里就可以得出結(jié)論了:

  1. pop之后荷并,所有child page肯定都為空了合砂,且當(dāng)前page一定是hotPage
  2. 系統(tǒng)為了節(jié)約內(nèi)存,判斷源织,如果當(dāng)前page空間使用少于一半翩伪,就釋放掉所有的child page,如果當(dāng)前page空間使用大于一半谈息,就從孫子page開始釋放缘屹,預(yù)留一個(gè)child page

歡迎關(guān)注我的博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末侠仇,一起剝皮案震驚了整個(gè)濱河市轻姿,隨后出現(xiàn)的幾起案子犁珠,更是在濱河造成了極大的恐慌,老刑警劉巖互亮,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件犁享,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡豹休,警方通過(guò)查閱死者的電腦和手機(jī)炊昆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)威根,“玉大人凤巨,你說(shuō)我怎么就攤上這事÷宀螅” “怎么了敢茁?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)姥卢。 經(jīng)常有香客問我卷要,道長(zhǎng),這世上最難降的妖魔是什么独榴? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮奕枝,結(jié)果婚禮上棺榔,老公的妹妹穿的比我還像新娘。我一直安慰自己隘道,他們只是感情好症歇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谭梗,像睡著了一般忘晤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上激捏,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天设塔,我揣著相機(jī)與錄音,去河邊找鬼远舅。 笑死闰蛔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的图柏。 我是一名探鬼主播序六,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蚤吹!你這毒婦竟也來(lái)了例诀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎繁涂,沒想到半個(gè)月后暮刃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡爆土,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年椭懊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片步势。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡氧猬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出坏瘩,到底是詐尸還是另有隱情盅抚,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布倔矾,位于F島的核電站妄均,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏哪自。R本人自食惡果不足惜丰包,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望壤巷。 院中可真熱鬧邑彪,春花似錦、人聲如沸胧华。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)矩动。三九已至有巧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悲没,已是汗流浹背篮迎。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留檀训,地道東北人柑潦。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像峻凫,于是被迫代替她去往敵國(guó)和親渗鬼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 不管你是新手還是老鳥差牛,工作中肯定逃不開autoreleasepool這個(gè)天使,因?yàn)樗龑?shí)在是一門藝術(shù)堰乔,為我們現(xiàn)在的編...
    皇垚閱讀 2,564評(píng)論 0 3
  • ARC下偏化,我們使用@autoreleasepool{}來(lái)使用一個(gè)AutoreleasePool void main...
    cmhfx1閱讀 745評(píng)論 0 2
  • 因?yàn)楝F(xiàn)在普遍使用ARC,所以項(xiàng)目中幾乎看不到release這樣的字眼了镐侯,但是在一個(gè)不起眼的地方 —— main.m...
    Haven_ZN閱讀 2,042評(píng)論 6 16
  • <!DOCTYPE html> slider div,ul,li,body,img{padding: ...
    debt閱讀 198評(píng)論 0 0
  • 天承地合共連理侦讨,廿六年來(lái)終題名! 申呼酉喚慶伉儷苟翻,初九日里賓客迎韵卤。
    空無(wú)明閱讀 202評(píng)論 0 0