iOS中autorelease的那些事兒

前言

在MRC下, 我們需要手動管理內存, 寫一大堆的retain, release代碼, 稍不留神就會造成內存泄露; 而ARC下, 編譯器幫我們屏蔽掉了這些繁瑣的代碼, 我們不需要再一條一條地寫retain, release了, 可以專心地把精力放在業務邏輯, 技術上.
在MRC下, 調用[object autorelease]可以延遲對象的內存釋放; 在ARC下, 我們甚至可以不需要知道 autorelease 是什么都能管理好內存. 編譯器幫我們做了什么事情? 到底 autorelease 有什么神奇的地方? autorelease pool 又是個什么東西?下面我將會一一道來.

NSAutoreleasePool 與 @autoreleasepool

NSAutoreleasePool 是 Cocoa 用來支持引用計數內存管理機制的類, 當一個autorelease pool(自動釋放池)被drain(銷毀)的時候會對pool里的對象發送一條release的消息.
注意 : 在ARC下, 不能使用NSAutoreleasePool這個類來創建自動釋放池, 而應該用@autoreleasepool { } 這個block, 官方文檔源碼如下 :

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Code benefitting from a local autorelease pool.
[pool release];

用以下代碼代替上述代碼 :

@autoreleasepool {
    // Code benefitting from a local autorelease pool.
}
Ps : 官方文檔說明, 使用@autoreleasepool這個block比NSAutoreleasePool更高效!并且在MRC環境下同樣適用*

讓我們用一張圖片來了解對象調用autorelease的整個過程


Object autorelease

補充幾點 :

  1. 蘋果官方文檔說An object can be put into the same pool several times, in which case it receives a release message for each time it was put into the pool. 意思是一個對象可以多次放入同一池子中, 并且每次放進去的時候都會調用release方法.. 但是我在MRC下做了測試, 結果是每調用一次autorelease方法, 池子中就多一個對象. 調用5次之后打印池子, 發現池子中有5個相同的對象.... 有知道的朋友麻煩告訴下謝謝
  2. 程序中至少存在一個自動釋放池, 否則autoreleased對象將不能對應收到release消息而導致內存泄露.


    自動釋放池
  3. NSAutoreleasePool對象不能retain, 不能autorelease, 所以drain方法(或者release方法, 但是這兩者有所不同, 下文會說)可以直接釋放內存. 你應該在同一個上下文(調用創建這個池的同一個方法, 函數或者循環體)中drain一個自動釋放池.
  4. MRC下需要對象調用autorelease才會入池, ARC下可以通過__autoreleasing修飾符, 否則的話看方法名, 非alloc/new/copy/mutableCopy開頭的方法編譯器都會自動幫我們調用autorelease方法.
  5. 不一定要自己創建自動釋放池, 但是有3種情況下是很必要的, 下面會講.
  6. 自動釋放池可以嵌套使用

下面講autorelease pool 與 線程, RunLoop的關系...

autorelease pool 與 線程

每一個線程(包括主線程)都有一個NSAutoreleasePool棧. 當一個新的池子被創建的時候, push進棧. 當池子被釋放內存時, pop出棧. 對象調用autorelease方法進入棧頂的池子中. 當線程結束的時候, 它會自動地銷毀掉所有跟它有關聯的池子.

線程中的自動釋放池棧

如果你的應用或者線程是長期存在的并且有可能產生大量的autoreleased對象, 你應該定期地drain和create自動釋放池, 否則, autorelease對象會在內存中堆積造成內存告急. 這里借用土土哥的一張圖,
循環體中是否使用autoreleasepool的區別

測試的內容:500000次循環,每次循環創建一個NSNumber實例和兩個NSString實例。
圖:紅線表示沒有用@autoreleasepool時的內存占用。
圖:綠線表示用了@autoreleasepool優化后的內存占用!

如上圖所示, 在每一次循環中, 先創建一個自動釋放池, 然后循環結束的時候, 自動釋放池銷毀, release掉池中對象, 釋放內存. 對比之下, 優劣不言而喻.
蘋果也是這么做的, 數組的block遍歷方法就是, 大家可以自行測試.

數組的block遍歷方法

autorelease pool 與 RunLoop

autorelease pool 與 RunLoop

程序運行 -> 開啟事件循環 -> 發生觸摸事件 -> 創建自動釋放池 -> 處理觸摸事件 -> 事件對象加入自動釋放池 -> 一次事件循環結束, 銷毀自動釋放池.

蘋果官方文檔說 :
The Application Kit creates an autorelease pool on the main thread at the beginning of every cycle of the event loop, and drains it at the end, thereby releasing any autoreleased objects generated while processing an event

在開始每一個事件循環之前系統會在主線程創建一個自動釋放池, 并且在事件循環結束的時候把前面創建的釋放池釋放, 回收內存. 這里不深入講解RunLoop, 文章后面會給出幾篇RunLoop的文章, 大家可以去看看.

如何管理自動釋放池

這里介紹4個方法

  1. release
  2. drain
  3. autorelease
  4. retain

release 和 drain

這里把他們放在一塊講, 是因為他們在引用計數環境下都能銷毀一個自動釋放池, 為什么這里要特意說明引用計數環境, 因為在引用計數環境和垃圾回收(GC)環境下, 這兩個方法不盡相同
引用計數環境下, release 和 drain 效果相同, 均能銷毀一個自動釋放池.
垃圾回收環境下, drain同上, release 則是一個空方法.
所以建議為了兼容性, 統一用drain吧.

autorelease 和 retain

拋出異常, 因為NSAutoreleasePool不能調用以上 autorelease 和 retain 方法.

怎么使用autorelease pool

由于@autoreleasepool同時兼容MRC和ARC編譯環境(NSAutoreleasePool只能在MRC下使用), 所以以下均是以autorelease pool block來介紹使用.
Cocoa 希望代碼總是在autorelease pool block中被執行, 否則autoreleased對象就得不到釋放從而造成內存泄露.

什么時候需要自己手動創建autorelease pool

看蘋果官方文檔怎么說明 :

  1. If you are writing a program that is not based on a UI framework, such as a command-line tool.

  2. If you write a loop that creates many temporary objects.
    You may use an autorelease pool block inside the loop to dispose of those objects before the next iteration. Using an autorelease pool block in the loop helps to reduce the maximum memory footprint of the application.

  3. If you spawn a secondary thread.
    You must create your own autorelease pool block as soon as the thread begins executing; otherwise, your application will leak objects.

  4. 你寫的程序不是基于UI framework, 例如命令行項目

  5. 你寫的循環創建了大量臨時對象 -> 你需要在循環體內創建一個autorelease pool block并且在每次循環結束之前處理那些autoreleased對象. 在循環中使用autorelease pool block可以降低內存峰值

  6. 你創建了一個新線程
    當線程開始執行的時候你必須立馬創建一個autorelease pool block, 否則你的應用會造成內存泄露.

使用場景 :

  1. 利用@autoreleasepool優化循環, 如上述提過的例子所示
  2. 如果你的應用程序或者線程是要長期運行的并且有可能產生大量autoreleased對象, 你應該使用autorelease pool blocks
  3. 長期在后臺中運行的任務, 方法

使用方法 : 不要太簡單~~

    @autoreleasepool {
        // Code here
    }
這里介紹一種特殊的情況

先上蘋果官方源碼

– (id)findMatchingObject:(id)anObject {
    id match;
    while (match == nil) {
      @autoreleasepool {
      // Do a search that creates a lot of temporary objects. 
      match = [self expensiveSearchForObject:anObject];
      if (match != nil) {
        [match retain]; /* Keep match around. */
        }
      }
    }
    return [match autorelease]; /* Let match go and return it. */
}

在block結束之后, 你要注意的是任何autoreleased對象已經被處理過了(release). 請不要對這個對象發送消息或者把這個對象當做方法的返回值返回. 會引發野指針錯誤.
解決方法 : 蘋果是這么做的 : 在block內對match對象發送retain消息和在block外對match發送autorelease消息能延長match對象的生命周期并且允許match對象在block外部接收消息或者作為方法的返回值返回. 我們不需要再關心match什么時候釋放, 因為它已經交給了上一層的autorelease pool去管理.

參考文檔 :
NSAutoreleasePool Class Reference
Using Autorelease Pool Blocks
黑幕背后的Autorelease
@autoreleasepool-內存的分配與釋放

這里推薦兩個RunLoop的文章
深入理解RunLoop
RunLoop


歡迎大家關注@Jerry4me, 同時本文有錯漏的點懇請大家不吝指出, 謝謝~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容