翻譯:關(guān)于內(nèi)存管理(二)——實際內(nèi)存管理(Practical Memory Management)

盡管內(nèi)存管理策略( Memory Management Policy)中描述的基本概念比較簡單世杀,但還是有一些實際步驟可簡化內(nèi)存管理,并幫助你確保應(yīng)用是可靠的、健壯的,同時最大限度的減少其資源需求厘托。

使用訪問器方法來簡化內(nèi)存管理

如果類有個屬性是對象,你必須確保在使用它的過程中未被釋放稿湿。因此你必須在set時聲明該對象的所有權(quán)铅匹。你還必須確保之后放棄當(dāng)前持有值的所有權(quán)。

有時候看起來乏味或者迂腐饺藤,但如果一致使用訪問器方法包斑,與內(nèi)存管理相關(guān)的問題可能性大大降低。如果你在代碼中retainrelease實例變量涕俗,那么肯定你做了件錯事舰始。

考慮你想set的計數(shù)器對象
<pre><code>
@interface Counter : NSObject

@property (nonatomic, retain) NSNumber *count;

@end;

</pre></code>

property聲明兩個訪問器方法。通常情況下咽袜,你應(yīng)該訪問編譯器合成該方法,然而枕稀,看看他們是如何實現(xiàn)的是有益的询刹。

在get訪問器中,返回合成實例變量萎坷,所以沒有必要retainrelease凹联。
<pre><code>
-(NSNumber *)count {

return _count;

}

</pre></code>

在set方法中,如果每個人按照相同的規(guī)則:假設(shè)新計數(shù)可能隨時被處理哆档,必須通過發(fā)送retain消息獲取對象的所有權(quán)蔽挠,確保新計數(shù)不被處理。通過發(fā)送release消息,放棄舊計數(shù)對象的所有權(quán)澳淑。(發(fā)送消息到nil在Objective-C是允許的比原,所以實現(xiàn)仍然正常即便_count尚未設(shè)置。)你必須在 [newCount retain]后發(fā)送杠巡,防止兩者是相同的對象——你不想無意中使它被回收量窘。
<pre><code>
-(void)setCount:(NSNumber *)newCount {

[newCount retain];

[_count release];

// Make the new assignment.

_count = newCount;

}

</pre></code>

使用訪問器方法設(shè)置屬性值

假設(shè)你想實現(xiàn)一個重置計數(shù)器的方法。你有兩種選擇氢拥。第一種實現(xiàn)使用alloc創(chuàng)建NSNumber實例蚌铜,所以使用release釋放。
<pre><code>
-(void)reset {

NSNumber *zero = [[NSNumber alloc] initWithInteger:0];

[self setCount:zero];

[zero release];

}
</pre></code>

第二種使用一個方便的構(gòu)造函數(shù)創(chuàng)建一個新NSNumber對象嫩海。因此不需要retainrelease消息冬殃。

<pre><code>
-(void)reset {

NSNumber *zero = [NSNumber numberWithInteger:0];

[self setCount:zero];

}

</pre></code>

注意,兩種方法都是用訪問器方法叁怪。

以下代碼在簡單情況下肯定能正常工作审葬,但有可能避開訪問器方法,這樣做在某個階段會導(dǎo)致錯誤(例如骂束,當(dāng)您忘記retain或release耳璧,或者實例變量的內(nèi)存管理語義發(fā)生變化)。
<pre><code>
-(void)reset {

NSNumber *zero = [[NSNumber alloc] initWithInteger:0];

[_count release];

_count = zero;

}
</pre></code>

還要注意使用KVO展箱,這種改變變量的方式不兼容KVO旨枯。

不在初始化方法和dealloc中使用訪問器方法

在初始化方法和dealloc不應(yīng)該使用訪問器方法設(shè)置實例變量。使用代表0 的number對象初始化計數(shù)器對象混驰,可以如下實現(xiàn)init方法:

<pre><code>
-init {

self = [super init];

if (self) {

    _count = [[NSNumber alloc] initWithInteger:0];

}

return self;

}

</pre></code>

初始化計數(shù)器的計數(shù)不為0攀隔,可以如下實現(xiàn)initWithCount:方法。

<pre><code>
-initWithCount:(NSNumber *)startingCount {

self = [super init];

if (self) {

    _count = [startingCount copy];

}


return self;

}
</pre></code>

由于計數(shù)器對象有實例變量栖榨,還必須實現(xiàn)dealloc方法昆汹。通過發(fā)送release消息放棄所有實例變量的所有權(quán),并最終調(diào)用super的實現(xiàn):

<pre><code>
-(void)dealloc {

[_count release];

[super dealloc];

}

</pre></code>

使用弱引用避免循環(huán)引用

創(chuàng)建對象的一個強引用可以保留對象婴栽。一個對象不能被回收直到所有的強引用都被是否满粗。如果兩個對象有循環(huán)引用將導(dǎo)致循環(huán)引用問題,他們彼此有一個強引用(直接強引用或通過一連串其他對象的強引用會到第一個對象)愚争。

圖1所示對象關(guān)系說明了一個潛在的循環(huán)引用映皆。每個Document對象中的每個頁面有個Page對象。每個Page對象有一個屬性跟蹤它所在的文檔轰枝。如果Document對象有Page對象的強引用捅彻,而Page對象對Document對象也有強引用,則兩個對象都不能 被釋放鞍陨。Document的引用計數(shù)器不能變成0直到Page對象被釋放步淹,同時Page對象不能被釋放直到Document對象被回收。

圖1 循環(huán)引用圖示

解決循環(huán)引用問題可以使用弱引用。弱引用不擁有源對象的關(guān)系缭裆,當(dāng)有引用時不retain對象键闺。

為了保持對象圖的完整,然而幼驶,必須在某處有強引用(如果只有弱引用艾杏,那么頁面和段落可能沒有所有者,因此會被回收)盅藻。Cocoa建立一個慣例购桑,因此,一個“父”對象必須對其“zi”對象保持強引用氏淑,“子”對象必須對“父”對象是弱引用勃蜘。

所以,在圖1中假残,Document對象有一個強引用(retain)其Page對象缭贡,但Page對象有一個弱引用(不retain)Document對象。

Cocoa中弱引用的例子包括但不限于表數(shù)據(jù)源辉懒,大綱視圖阳惹,通知觀察者,其他目標(biāo)和代理眶俩。

當(dāng)你只有弱引用對象時莹汤,發(fā)送消息要小心。如果你在對象被回收之后發(fā)送消息颠印,app會崩潰纲岭。當(dāng)對象可用時必須有明確的條件。在大多數(shù)情況下线罕,弱引用對象知道其他對象的弱引用止潮,為了防止循環(huán)引用,當(dāng)被回收時負(fù)責(zé)通知其他對象钞楼。例如喇闸,當(dāng)你注冊通知中心對象,通知中心存儲該對象的弱引用并在特定通知發(fā)布后發(fā)送消息询件。當(dāng)對象被回收燃乍,需要在通知中心注銷防止通知中心發(fā)送消息到該對象。同樣的雳殊,當(dāng)代理對象被回收,通過發(fā)送nil參數(shù)的setDelegate: 消息到其他對象刪除該代理窗轩。這些消息通常從對象的dealloc方法中發(fā)送夯秃。

避免回收正在使用的對象

Cocoa的所有權(quán)策略通常指接收對象應(yīng)該在整個調(diào)用過程中保持有效。它也有可能返回一個當(dāng)前范圍內(nèi)不會被釋放的接收對象。它不能影響app對象的getter方法返回一個緩存實例變量或一個計算值仓洼。重要的是該對象在你需要使用的時候仍然是有效的介陶。

偶爾有例外,主要是這兩種情況色建。
1.當(dāng)一個對象從基本collection classes中刪除哺呜。
<pre><code>
heisenObject = [array objectAtIndex:n];

[array removeObjectAtIndex:n];

// heisenObject could now be invalid.

</pre></code>

當(dāng)一個對象從基本集合類中刪除,發(fā)送一個release(而不是autorelease)消息箕戳。如果集合是刪除對象的唯一所有者某残,刪除對象(在例子中的`heisenObject`)立馬被回收。

2.當(dāng)“父對象”被回收陵吸。
<pre><code>
id parent = <#create a parent object#>;

// ...

heisenObject = [parent child] ;

[parent release]; // Or, for example: self.parent = nil;

// heisenObject could now be invalid.

</pre></code>

在某些情況下玻墅,從另一個對象中檢索一個對象,然后直接或間接釋放父對象壮虫。如果釋放父對象導(dǎo)致其被回收澳厢,并且父對象是子對象的唯一所有者,則子對象(例子中的heisenObject)會同時被回收(假設(shè)在父對象的dealloc方法中發(fā)送一個release而不是autorelease消息)囚似。
為了防止這些情況剩拢,在接收到heisenObject時retain,在完成時饶唤,release徐伐。例如:
<pre><code>
heisenObject = [[array objectAtIndex:n] retain];

[array removeObjectAtIndex:n];

// Use heisenObject...

[heisenObject release];

</pre></code>

不要使用dealloc管理稀缺資源

通常不需要在dealloc方法中管理稀缺資源如文件描述符、網(wǎng)絡(luò)連接搬素、緩存或緩存呵晨。特別是,不要設(shè)計在dealloc中會調(diào)用的類熬尺。dealloc的調(diào)用可能因為一個錯誤或應(yīng)用中斷被推遲或回避摸屠。

相反,如果你有一個類粱哼,它的實例管理稀缺資源時季二,應(yīng)該設(shè)計應(yīng)用,在你不再需要資源時可以告訴實例需要清理揭措。通常釋放該實例胯舷,然后dealloc,如果沒有dealloc绊含,你也不會遇到其他問題桑嘶。

如果你嘗試在dealloc中管理資源可能出現(xiàn)問題。例如:

  1. 順序依賴對象圖銷毀

  2. 對象圖銷毀本質(zhì)上是無序的躬充。盡管通常你預(yù)期得到一個特定的屬性逃顶,你的引用是脆弱的讨便。如果對象意外自動釋放而不是釋放,則釋放順序可能改變以政,這可能導(dǎo)致意想不到的結(jié)果霸褒。

  3. 不回收稀缺資源

  4. 內(nèi)存泄露是bug必須修復(fù),但他們通常不會立即死亡盈蛮。如果稀缺資源不立即釋放废菱,然而,你會遇到更嚴(yán)重的問題抖誉。如果應(yīng)用耗盡了文件描述符殊轴,例如,用戶可能無法保存數(shù)據(jù)寸五。

  5. 在錯誤的線程上執(zhí)行邏輯清理梳凛。

  6. 如果對象在意想不到的時間被自動釋放,在它所在的線程自動釋放池block中被回收梳杏。這種只能從一個線程訪問的資源是致命的韧拒。

集合擁有他們包含的對象

當(dāng)你往集合(例如array、dictionary或set)中添加對象十性,集合會擁有該對象的所有權(quán)叛溢。當(dāng)該對象從集合中刪除或集合本身被釋放時,集合會放棄所有權(quán)劲适。因此楷掉,例如,如果你想創(chuàng)建數(shù)字?jǐn)?shù)組霞势,你可以按照以下實現(xiàn):
<pre><code>
NSMutableArray *array = <#Get a mutable array#>;

NSUInteger i;

// ...

for (i = 0; i < 10; i++) {

NSNumber *convenienceNumber = [NSNumber numberWithInteger:i];

[array addObject:convenienceNumber];

}
</pre></code>

在這種情況下烹植,你沒有調(diào)用alloc,所以沒有必要調(diào)用release愕贡。也沒有必要retain新數(shù)據(jù)(convenienceNumber)草雕,因為數(shù)組將釋放。

<pre><code>
NSMutableArray *array = <#Get a mutable array#>;

NSUInteger i;

// ...

for (i = 0; i < 10; i++) {

NSNumber *allocedNumber = [[NSNumber alloc] initWithInteger:i];

[array addObject:allocedNumber];

[allocedNumber release];

}
</pre></code>

在這種情況固以,你需要在for循環(huán)的范圍內(nèi)發(fā)送release消息以此來抵消alloc墩虹。因為通過addObject:往數(shù)組中添加對象時,數(shù)組retain該對象憨琳,該對象只要在數(shù)組中就不會被回收诫钓。

要理解這點,把自己放到實現(xiàn)集合類的人的位置篙螟。你想確保不需要提供對象菌湃,所以當(dāng)他們被傳入時,向他們發(fā)送retain消息遍略。如果他們被刪除惧所,你必須發(fā)送release消息场梆,在dealloc方法中給其他保留的對象發(fā)送release消息。

使用Retain Counts實現(xiàn)所有權(quán)策略

所有權(quán)策略是通過引用計數(shù)實現(xiàn)纯路,通常稱為“retain count”。每個對象都有retain count寞忿。

  • 當(dāng)你創(chuàng)建一個對象驰唬,它的retain count為1.

  • 當(dāng)你給對象發(fā)送retain消息,它的retain count增加1.

  • 當(dāng)你給對象發(fā)送release消息腔彰,它的retain count減1.

  • 當(dāng)你給對象發(fā)送autorelease消息叫编,它的retain count在當(dāng)前自動釋放池block的最后減1。

  • 如果對象的retain count減到0霹抛,它被回收搓逾。

重要:沒有明確理由訪問一個對象的retain count(參見retainCount)。結(jié)果往往是誤導(dǎo)杯拐,因為你可能沒有意識到礦建對象retain你要的對象霞篡。在調(diào)試內(nèi)存管理問題,你應(yīng)該只關(guān)心你的代碼是否堅持所有權(quán)規(guī)則端逼。

官方原文地址:

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW1

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末朗兵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子顶滩,更是在濱河造成了極大的恐慌余掖,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件礁鲁,死亡現(xiàn)場離奇詭異盐欺,居然都是意外死亡,警方通過查閱死者的電腦和手機仅醇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門冗美,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人着憨,你說我怎么就攤上這事墩衙。” “怎么了甲抖?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵漆改,是天一觀的道長。 經(jīng)常有香客問我准谚,道長挫剑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任柱衔,我火速辦了婚禮樊破,結(jié)果婚禮上愉棱,老公的妹妹穿的比我還像新娘。我一直安慰自己哲戚,他們只是感情好奔滑,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著顺少,像睡著了一般朋其。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脆炎,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天梅猿,我揣著相機與錄音,去河邊找鬼秒裕。 笑死袱蚓,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的几蜻。 我是一名探鬼主播喇潘,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼梭稚!你這毒婦竟也來了响蓉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤哨毁,失蹤者是張志新(化名)和其女友劉穎枫甲,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扼褪,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡想幻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了话浇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脏毯。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖幔崖,靈堂內(nèi)的尸體忽然破棺而出食店,到底是詐尸還是另有隱情,我是刑警寧澤赏寇,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布吉嫩,位于F島的核電站,受9級特大地震影響嗅定,放射性物質(zhì)發(fā)生泄漏自娩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一渠退、第九天 我趴在偏房一處隱蔽的房頂上張望忙迁。 院中可真熱鬧脐彩,春花似錦、人聲如沸姊扔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恰梢。三九已至晨川,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間删豺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工愧怜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留呀页,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓拥坛,卻偏偏與公主長得像蓬蝶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子猜惋,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

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

  • 29.理解引用計數(shù) Objective-C語言使用引用計數(shù)來管理內(nèi)存丸氛,也就是說,每個對象都有個可以遞增或遞減的計數(shù)...
    Code_Ninja閱讀 1,475評論 1 3
  • 內(nèi)存管理 簡述OC中內(nèi)存管理機制著摔。與retain配對使用的方法是dealloc還是release缓窜,為什么?需要與a...
    丶逐漸閱讀 1,950評論 1 16
  • 內(nèi)存管理是程序在運行時分配內(nèi)存谍咆、使用內(nèi)存禾锤,并在程序完成時釋放內(nèi)存的過程。在Objective-C中摹察,也被看作是在眾...
    蹲瓜閱讀 3,027評論 1 8
  • iOS內(nèi)存管理 概述 什么是內(nèi)存管理 應(yīng)用程序內(nèi)存管理是在程序運行時分配內(nèi)存(比如創(chuàng)建一個對象,會增加內(nèi)存占用)與...
    蚊香醬閱讀 5,704評論 8 119
  • Commons DbUtils是Apache組織提供的一個對JDBC進(jìn)行簡單封裝的開源工具類庫恩掷,使用它能夠簡化應(yīng)用...
    臂力棒在想閱讀 207評論 0 0