autorelease和autoreleasePoolPage--你真的了解么?

一 .autorelease對(duì)象的釋放時(shí)機(jī)
二.AutoreleasePoolPage對(duì)象
三. autorelease與RunLoop的關(guān)系

一 .autorelease對(duì)象的釋放時(shí)機(jī)

@autoreleasepool即自動(dòng)釋放池,在自動(dòng)釋放池中聲明的對(duì)象如果調(diào)用了autorelase方法,那么當(dāng)@autoreleasepool代碼塊執(zhí)行完之后,在塊中調(diào)用過autorelease方法的對(duì)象都會(huì)自動(dòng)調(diào)用一次release方法户魏。

比如下面的代碼:

打印結(jié)果:


可以看到打印結(jié)果也驗(yàn)證了我們上面所說的,但是事實(shí)確實(shí)如此么?或者說Person對(duì)象是在執(zhí)行到第23行的時(shí)候調(diào)用了release方法 這個(gè)說法準(zhǔn)確么?

為了更好的說明問題我們?cè)賮砜匆粋€(gè)小例子:


上面的代碼中Person對(duì)象是在第28行銷毀么?

我們來看一下打印結(jié)果就知道了:


可以看到Person對(duì)象并沒有在viewDidLoad執(zhí)行完銷毀,為什么會(huì)這樣呢?

為了搞明白這些我們有必要來對(duì) autorelease的內(nèi)部做一些深入的探究.....

通過下面的命令將我們的.m文件轉(zhuǎn)化成.cpp文件

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

打開main.cpp文件,拖到最后,我們可以看到生成的C++代碼如下所示:

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { 
    __AtAutoreleasePool __autoreleasepool; 
        Person *per = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init")), sel_registerName("autorelease"));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_9n_sqylftm50nv_y8jbf4089_fr0000gn_T_main_885529_mi_1);
    }
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_9n_sqylftm50nv_y8jbf4089_fr0000gn_T_main_885529_mi_2);
    return 0;
}

其實(shí)重點(diǎn)就是 __AtAutoreleasePool這個(gè)對(duì)象,我們來看看該對(duì)象的結(jié)構(gòu):

struct __AtAutoreleasePool {
    //構(gòu)造函數(shù),在創(chuàng)建該結(jié)構(gòu)體的時(shí)候調(diào)用
    __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
    
    //析構(gòu)函數(shù),在銷毀該結(jié)構(gòu)體的時(shí)候調(diào)用
    //將objc_autoreleasePoolPush()返回的結(jié)果傳入到objc_autoreleasePoolPop()中
    ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
    void * atautoreleasepoolobj;
};

它其實(shí)就是一個(gè)結(jié)構(gòu)體對(duì)象,內(nèi)部包括了一個(gè)構(gòu)造函數(shù)和一個(gè)析構(gòu)函數(shù).
objc_autoreleasePoolPush()objc_autoreleasePoolPop() 又是什么呢太防?

我們可以從Runtime源碼中看到他們的實(shí)現(xiàn)如下:


通過上圖我們可以看到這個(gè)兩個(gè)方法內(nèi)部分別調(diào)用了 AutoreleasePoolPage對(duì)象的push()pop()方法,所以調(diào)用了autorelease的對(duì)象最終都是通過AutoreleasePoolPage對(duì)象來管理的

2. AutoreleasePoolPage

關(guān)于AutoreleasePoolPage對(duì)象我們需要知道下面兩點(diǎn):

  1. 每個(gè)AutoreleasePoolPage對(duì)象占用4096個(gè)字節(jié)的內(nèi)存,除了用來存放它內(nèi)部的成員變量外串纺,剩下的空間用來存放autorelease對(duì)象的地址
  2. 所有的AutoreleasePoolPage對(duì)象都是通過雙向鏈表的形式連接在一起

內(nèi)部結(jié)構(gòu):
通過源碼我們可以看到AutoreleasePoolPage對(duì)象的大概結(jié)構(gòu)如下圖所示:

  1. id *next 指向了下一個(gè)能存放autorelease對(duì)象地址的區(qū)域 如下圖:

  2. parent 父節(jié)點(diǎn) 指向前一個(gè)page

  3. child 子節(jié)點(diǎn) 指向下一個(gè)page ,如下圖

  4. POOL_BOUNDARY是一個(gè)邊界對(duì)象 nil,之前的源代碼變量名是 POOL_SENTINEL哨兵對(duì)象,用來區(qū)別每個(gè)page即每個(gè) AutoreleasePoolPage邊界

AutoreleasePoolPage::push()
調(diào)用push方法會(huì)將一個(gè)POOL_BOUNDARY入棧棺耍,并且返回其存放的內(nèi)存地址.
push就是壓棧的操作,先加入邊界對(duì)象,然后添加person1對(duì)象,然后是person2對(duì)象...以此類推

AutoreleasePoolPage::pop(ctxt);
調(diào)用pop方法時(shí)傳入一個(gè)POOL_BOUNDARY的內(nèi)存地址,會(huì)從最后一個(gè)入棧的對(duì)象開始發(fā)送release消息宏多,直到遇到這個(gè)POOL_BOUNDARY(因?yàn)槭请p向鏈表,所以可以向上尋找)

3. autorelease與RunLoop

通過以上的分析我們大概了解了autorelease的內(nèi)部結(jié)構(gòu)以及執(zhí)行原理,但是你還是沒有說明使用autorelease的對(duì)象是在什么時(shí)候釋放的啊?別著急 慢慢來....

在應(yīng)用程序剛啟動(dòng)的時(shí)候我們打印當(dāng)前的RunLoop對(duì)象 (關(guān)于RunLoop對(duì)象的使用以及分析可以看看我的這篇文章RunLoop的使用)

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"%@",[NSRunLoop currentRunLoop]);
}

部分結(jié)果如下:


由上圖我們可以看到iOS應(yīng)用啟動(dòng)后RunLoop會(huì)注冊(cè)兩個(gè)Observer來管理和維護(hù)AutoreleasePool
我們知道 RunLoop有如下這幾種狀態(tài):

   typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry = (1UL << 0),         // 0 進(jìn)入RunLoop 
        kCFRunLoopBeforeTimers = (1UL << 1),  // 2 即將開始Timer處理
        kCFRunLoopBeforeSources = (1UL << 2), // 4 即將開始Source處理
        kCFRunLoopBeforeWaiting = (1UL << 5), // 32 即將進(jìn)入休眠
        kCFRunLoopAfterWaiting = (1UL << 6),  // 64 從休眠狀態(tài)喚醒
        kCFRunLoopExit = (1UL << 7),          // 128 退出RunLoop
        kCFRunLoopAllActivities = 0x0FFFFFFFU
    };

可以看到 activities = 0x1監(jiān)測(cè)的是kCFRunLoopEntry也就是進(jìn)入RunLoop的狀態(tài),此時(shí)它會(huì)回調(diào)objc_autoreleasePoolPush()方法向當(dāng)前的AutoreleasePoolPage增加一個(gè)POOL_BOUNDARY標(biāo)志創(chuàng)建自動(dòng)釋放池疯攒。

activities = 0xa0監(jiān)測(cè)的是kCFRunLoopBeforeWaitingkCFRunLoopExit兩種狀態(tài).
kCFRunLoopBeforeWaiting(即將進(jìn)入休眠)時(shí)會(huì)調(diào)用objc_autoreleasePoolPop()objc_autoreleasePoolPush()方法. 系統(tǒng)會(huì)根據(jù)情況從最新加入的對(duì)象一直往前清理直到遇到POOL_BOUNDARY標(biāo)志
而在即將退出RunLoop時(shí)會(huì)調(diào)用objc_autoreleasePoolPop() 方法釋放自動(dòng)釋放池內(nèi)對(duì)象嗦随。

所以autorelease的釋放時(shí)機(jī)取決于RunLoop的運(yùn)行狀態(tài).

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市敬尺,隨后出現(xiàn)的幾起案子枚尼,更是在濱河造成了極大的恐慌,老刑警劉巖砂吞,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件署恍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蜻直,警方通過查閱死者的電腦和手機(jī)盯质,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來概而,“玉大人呼巷,你說我怎么就攤上這事∈旯澹” “怎么了王悍?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)餐曼。 經(jīng)常有香客問我配名,道長(zhǎng),這世上最難降的妖魔是什么晋辆? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮宇整,結(jié)果婚禮上瓶佳,老公的妹妹穿的比我還像新娘。我一直安慰自己鳞青,他們只是感情好霸饲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著臂拓,像睡著了一般厚脉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胶惰,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天傻工,我揣著相機(jī)與錄音,去河邊找鬼。 笑死中捆,一個(gè)胖子當(dāng)著我的面吹牛鸯匹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泄伪,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼殴蓬,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蟋滴?” 一聲冷哼從身側(cè)響起染厅,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎津函,沒想到半個(gè)月后肖粮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡球散,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年尿赚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蕉堰。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凌净,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屋讶,到底是詐尸還是另有隱情冰寻,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布皿渗,位于F島的核電站斩芭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏乐疆。R本人自食惡果不足惜划乖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挤土。 院中可真熱鬧琴庵,春花似錦、人聲如沸仰美。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)咖杂。三九已至庆寺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诉字,已是汗流浹背懦尝。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工知纷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人导披。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓屈扎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親撩匕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鹰晨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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