iOS 中AutoreleasePool實現(xiàn)原理上

我們都知道iOS的內(nèi)存管理分為手動內(nèi)存管理(MRC)和自動內(nèi)存管理(ARC)橙喘,但是不管是手動內(nèi)存管理還是自動內(nèi)存管理,自動釋放池在其中都起到至關(guān)重要的作用

我們首先看下官方文檔對自動釋放池的定義:

An object that supports Cocoa’s reference-counted memory management system.

從官方的定義中我們可以知道谚中,自動釋放池就是一個系統(tǒng)OC對象渴杆,它是作用于內(nèi)存管理中

下面我們先來分析官方文檔中對自動釋放池的一些解釋說明,這樣對我們深刻理解自動釋放池的工作原理非常有幫助

官方文檔地址:

我們先來看下官方對自動釋放池的聲明:

@interface NSAutoreleasePool : NSObject

在手動內(nèi)存管理和自動內(nèi)存管理模式下宪塔,創(chuàng)建自動釋放池的寫法也有所不同,

MRC模式下

    // 創(chuàng)建一個自動釋放池
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    NSObject *obj = [[NSObject alloc] init];
    
    // 將對象添加到自動釋放池
    [obj autorelease];
    
    // 銷毀自動釋放池囊拜,等同于`[pool release];`寫法某筐,但是`[pool release]`和`[pool drain]`又有一些不同,后面會講到不同點
    [pool drain];

或者

@autoreleasepool {
    NSObject *obj = [[NSObject alloc] init];
    
    // 將對象添加到自動釋放池
    [obj autorelease];
}

ARC模式下

@autoreleasepool {
    NSObject *obj = [[NSObject alloc] init];
    
    // 將對象添加到自動釋放池
    [obj autorelease];
}

從上面的創(chuàng)建方式我們可以看到冠跷,在ARC模式下南誊,只能使用@autoreleasepool{}這種方式創(chuàng)建自動釋放池,而不能使用NSAutoreleasePool這種創(chuàng)建OC對象的方式來創(chuàng)建

關(guān)于自動釋放池的創(chuàng)建方式蜜托,官方文檔也有詳細的介紹說明:

Important

If you use Automatic Reference Counting (ARC), you cannot use autorelease pools directly. Instead, you use @autoreleasepool blocks. For example, in place of:

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

you would write:

@autoreleasepool {
    // Code benefitting from a local autorelease pool.
}

@autoreleasepool blocks are more efficient than using an instance of NSAutoreleasePool directly; you can also use them even if you do not use ARC.

在自動釋放池的工作原理中抄囚,只有對象調(diào)用了autorelease方法,那么這個對象才會被添加到自動釋放池中橄务,然后當這個自動釋放池調(diào)用drain方法銷毀的時候幔托,便會向池中的每一個對象發(fā)送一條release消息來銷毀池中的對象,如果一個對象調(diào)用了autorelease多次,那么在自動釋放池銷毀后會向這個對象發(fā)送多條release消息來銷毀這個對象重挑,也就是說對象調(diào)用多少次autorelease嗓化,對象銷毀時就會調(diào)用多少次release

這里說明下一個對象調(diào)用release和調(diào)用autorelease的區(qū)別:

如果一個對象調(diào)用release則會立即釋放(如果引用計數(shù)減為0),如果一個對象調(diào)用autorelease則會先將這個對象放入自動釋放池谬哀,等到自動釋放池銷毀的時候在釋放這個對象刺覆。也就是說對象調(diào)用autorelease會延遲釋放

對應于官方文檔的解釋說明如下:

In a reference-counted environment (as opposed to one which uses garbage collection), an NSAutoreleasePool object contains objects that have received an autorelease message and when drained it sends a release message to each of those objects. Thus, sending autorelease instead of release to an object extends the lifetime of that object at least until the pool itself is drained (it may be longer if the object is subsequently retained). 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.

在引用計數(shù)的內(nèi)存管理方式下,Cocoa框架希望總是有一個自動釋放池可用史煎,如果沒有可用的自動釋放池谦屑,那么自動釋放的的對象得不到釋放就會造成內(nèi)存泄漏

對應于官方文檔的解釋說明如下:

In a reference counted environment, Cocoa expects there to be an autorelease pool always available. If a pool is not available, autoreleased objects do not get released and you leak memory. In this situation, your program will typically log suitable warning messages.

我們都知道iOS的應用程序,在程序一啟動的main函數(shù)中就創(chuàng)建了一個自動釋放池篇梭,并且在程序的主線程中添加了runloop氢橙,并且在runloop的每一個事件循環(huán)開始之前都會去創(chuàng)建自動釋放池,當自動釋放池對象調(diào)用drains時便會銷毀這個自動釋放池很洋,從而釋放在runloop處理事件過程中生成的所有對象充蓝。正是因為在runloop的事件循環(huán)中會自動創(chuàng)建自動釋放池,所以我們在平時開發(fā)過程中也不太需要開發(fā)者來手動創(chuàng)建自動釋放池喉磁,但是如果應用在runloop事件循環(huán)中創(chuàng)建了大量的臨時自動釋放對象谓苟,那么這時我們最好手動創(chuàng)建自動釋放池,將這大量的臨時對象放到手動創(chuàng)建的自動釋放池中

對應于官方文檔的解釋說明如下:

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. If you use the Application Kit, you therefore typically don’t have to create your own pools. If your application creates a lot of temporary autoreleased objects within the event loop, however, it may be beneficial to create “l(fā)ocal” autorelease pools to help to minimize the peak memory footprint.

這里需要注意:使用[[NSAutoreleasePool alloc] init]這種方式創(chuàng)建的自動釋放池對象协怒,我們不能對這個自動釋放池對象調(diào)用retain進行持有涝焙,我們也不能調(diào)用autorelease來釋放這個自動釋放池對象,我們要想銷毀這個自動釋放池對象孕暇,我們只能調(diào)用drain或者release仑撞。但是對于使用@autoreleasepool {}方式創(chuàng)建的自動釋放池,它會在出了作用域后自動銷毀創(chuàng)建的池子妖滔,不需要開發(fā)者的去調(diào)用函數(shù)銷毀

對應于官方文檔的解釋說明如下:

You create an NSAutoreleasePool object with the usual alloc and init messages and dispose of it with drain (or release—to understand the difference, see Garbage Collection). Since you cannot retain an autorelease pool (or autorelease it—see retain and autorelease), draining a pool ultimately has the effect of deallocating it. You should always drain an autorelease pool in the same context (invocation of a method or function, or body of a loop) that it was created. See Using Autorelease Pool Blocks for more details.

我們都知道在程序的運行過程中隧哮,可能會創(chuàng)建很多的自動釋放池,這些自動釋放池在應用程序中是以棧的方式來進行維護和管理的座舍,新創(chuàng)建的自動釋放池會添加到棧的頂部沮翔,當需要銷毀池子時,就從棧頂移除曲秉。并且每個線程都維護自己的自動釋放池棧采蚀,程序的主線程中有系統(tǒng)創(chuàng)建的自動釋放池,但是新創(chuàng)建的子線程默認是沒有自動釋放池的承二,如果子線程中需要自動釋放池榆鼠,則需要在子線程中手動創(chuàng)建自動釋放池,當子線程銷毀時亥鸠,也會自動銷毀子線程中創(chuàng)建的所有自動釋放池妆够。

對應于官方文檔的解釋說明如下:

Each thread (including the main thread) maintains its own stack of NSAutoreleasePool objects (see Threads). As new pools are created, they get added to the top of the stack. When pools are deallocated, they are removed from the stack. Autoreleased objects are placed into the top autorelease pool for the current thread. When a thread terminates, it automatically drains all of the autorelease pools associated with itself.

自動釋放池與線程之間的關(guān)系,官方文檔說明如下:

Threads

If you are making Cocoa calls outside of the Application Kit’s main thread—for example if you create a Foundation-only application or if you detach a thread—you need to create your own autorelease pool.

If your application or thread is long-lived and potentially generates a lot of autoreleased objects, you should periodically drain and create autorelease pools (like the Application Kit does on the main thread); otherwise, autoreleased objects accumulate and your memory footprint grows. If, however, your detached thread does not make Cocoa calls, you do not need to create an autorelease pool.

Note

If you are creating secondary threads using the POSIX thread APIs instead of NSThread objects, you cannot use Cocoa, including NSAutoreleasePool, unless Cocoa is in multithreading mode. Cocoa enters multithreading mode only after detaching its first NSThread object. To use Cocoa on secondary POSIX threads, your application must first detach at least one NSThread object, which can immediately exit. You can test whether Cocoa is in multithreading mode with the NSThread class method isMultiThreaded.

自動釋放池對象調(diào)用drainrelease來銷毀池子,它們二者的區(qū)別:

如果只是在引用計數(shù)環(huán)境下责静,那么調(diào)用drain和調(diào)用release功能上是一樣的袁滥,但是如果在垃圾回收(GC)環(huán)境下,調(diào)用release等于是一個no-op操作灾螃,no-op操作可以理解為沒有操作計算機指令题翻,而調(diào)用drain則會發(fā)出一個objc_collect_if_needed的GC操作。

所以說當我們不清楚什么時候會有GC操作時腰鬼,這時我們最好選擇使用drain來銷毀自動釋放池是最為安全的嵌赠。

對應于官方文檔的解釋說明如下:

Garbage Collection

In a garbage-collected environment, there is no need for autorelease pools. 
You may, however, write a framework that is designed to work in both a 
garbage-collected and reference-counted environment. In this case, you can use 
autorelease pools to hint to the collector that collection may be appropriate. 
In a garbage-collected environment, sending a drain message to a pool triggers
 garbage collection if necessary; release, however, is a no-op. In a reference-
counted environment, drain has the same effect as release. Typically, 
therefore, you should use drain instead of release.

上面我們對iOS的自動釋放池NSAutoreleasePool的官方文檔說明進行了理論性的分析和總結(jié),下面我們就從底層源碼來分析自動釋放池的底層結(jié)構(gòu)和實現(xiàn)原理

講解示例Demo地址:

https://github.com/guangqiang-liu/11-AutoreleasePool

https://github.com/guangqiang-liu/11.1-AutoreleasePool

https://github.com/guangqiang-liu/11.2-AutoreleasePool

https://github.com/guangqiang-liu/11.3-AutoreleasePool

更多文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末姜挺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子彼硫,更是在濱河造成了極大的恐慌炊豪,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拧篮,死亡現(xiàn)場離奇詭異词渤,居然都是意外死亡,警方通過查閱死者的電腦和手機串绩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門缺虐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人礁凡,你說我怎么就攤上這事高氮。” “怎么了顷牌?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵剪芍,是天一觀的道長。 經(jīng)常有香客問我窟蓝,道長紊浩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任疗锐,我火速辦了婚禮,結(jié)果婚禮上费彼,老公的妹妹穿的比我還像新娘滑臊。我一直安慰自己,他們只是感情好箍铲,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布雇卷。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪关划。 梳的紋絲不亂的頭發(fā)上小染,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音贮折,去河邊找鬼裤翩。 笑死,一個胖子當著我的面吹牛调榄,可吹牛的內(nèi)容都是我干的踊赠。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼每庆,長吁一口氣:“原來是場噩夢啊……” “哼筐带!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起缤灵,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤伦籍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后腮出,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帖鸦,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年利诺,在試婚紗的時候發(fā)現(xiàn)自己被綠了富蓄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡慢逾,死狀恐怖立倍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情侣滩,我是刑警寧澤口注,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站君珠,受9級特大地震影響寝志,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜策添,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一材部、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唯竹,春花似錦乐导、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旺拉。三九已至,卻和暖如春棵磷,著一層夾襖步出監(jiān)牢的瞬間蛾狗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工仪媒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沉桌,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓规丽,卻偏偏與公主長得像蒲牧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赌莺,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345