版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2017.10.07 |
前言
Core Foundation
框架(CoreFoundation.framework)
是一組C語言接口脐彩,它們?yōu)閕OS應(yīng)用程序提供基本數(shù)據(jù)管理和服務(wù)功能惠奸。接下來我們就詳細的解析這個框架梗掰。感興趣的可以看我上面寫的幾篇。
1. CoreFoundation框架詳細解析(一) —— 基本概覽
2. CoreFoundation框架詳細解析(二) —— 設(shè)計概念
3. CoreFoundation框架詳細解析(三) —— 內(nèi)存管理(一)
4. CoreFoundation框架詳細解析(四) —— 內(nèi)存管理(二)
About Memory Management - 關(guān)于內(nèi)存管理
應(yīng)用程序內(nèi)存管理是在程序運行時分配內(nèi)存的過程,使用它焚虱,并在完成后釋放內(nèi)存。 一個寫得好的程序盡可能少地使用內(nèi)存谍咆。 在Objective-C中,它也可以被看作是在許多數(shù)據(jù)和代碼之間分配有限的內(nèi)存資源的所有權(quán)的一種方式。 完成本指南后克滴,通過明確管理對象的生命周期并在不再需要時釋放它們,您將擁有管理應(yīng)用程序內(nèi)存所需的知識着帽。
雖然通常將內(nèi)存管理視為單個對象的級別,但您的目標實際上是管理object graphs.。 你想要確保你在內(nèi)存中沒有比實際需要更多的對象蕾羊。
1. At a Glance - 概覽
Objective-C提供了兩種應(yīng)用程序內(nèi)存管理方法。
- 在本指南中描述的方法(稱為
manual retain-release
)或MRR
中,通過跟蹤您擁有的對象來明確管理內(nèi)存哀澈。 這是使用基礎(chǔ)類NSObject與運行時環(huán)境結(jié)合提供的模型(稱為引用計數(shù))來實現(xiàn)的。 - 在自動引用計數(shù)或ARC中适荣,系統(tǒng)使用與MRR相同的引用計數(shù)系統(tǒng)比然,但在編譯時會為您插入適當?shù)膬?nèi)存管理方法調(diào)用丈氓。 強烈建議您使用ARC進行新項目。 如果您使用ARC强法,通常不需要了解本文檔中描述的基礎(chǔ)實現(xiàn)万俗,盡管在某些情況下可能會有所幫助。 有關(guān)ARC的更多信息饮怯,請參閱Transitioning to ARC Release Notes闰歪。
Good Practices Prevent Memory-Related Problems - 良好做法防止與內(nèi)存相關(guān)的問題
內(nèi)存管理不正確導(dǎo)致的主要問題有兩種:
- 釋放或重寫仍在使用的數(shù)據(jù)
- 這會導(dǎo)致內(nèi)存損壞课竣,并且通常會導(dǎo)致應(yīng)用程序崩潰,甚至導(dǎo)致用戶數(shù)據(jù)損壞。
- 不釋放不再使用的數(shù)據(jù)會導(dǎo)致內(nèi)存泄漏
- 內(nèi)存泄漏是分配的內(nèi)存不被釋放的地方佳遣,盡管它再也不會被使用。泄漏導(dǎo)致您的應(yīng)用程序使用不斷增加的內(nèi)存量杀糯,這反過來可能導(dǎo)致系統(tǒng)性能較差或您的應(yīng)用程序被終止冈欢。
從引用計數(shù)的角度考慮內(nèi)存管理往往適得其反,然而膏蚓,因為您傾向于根據(jù)實施細節(jié)而不是實際目標考慮內(nèi)存管理。相反,您應(yīng)該從對象所有權(quán)和 object graphs的角度考慮內(nèi)存管理蚯撩。
Cocoa使用一個簡單的命名約定來表示何時擁有一個方法返回的對象揭芍。
請參閱Memory Management Policy页衙。
盡管基本策略是直截了當?shù)囊诚欤梢圆扇∫恍嶋H步驟,使管理內(nèi)存更容易,并幫助確保程序保持可靠和穩(wěn)健檀夹,同時最大限度地減少其資源需求躲舌。
請參閱Practical Memory Management魄健。
自動釋放池塊提供了一種機制,您可以向?qū)ο蟀l(fā)送deferred release
發(fā)布消息。這在您想要放棄對象的所有權(quán)的情況下很有用谒麦,但是希望避免立即釋放它的可能性(例如從方法返回對象時)酣胀。有時您可能會使用自己的autorelease
池塊蒲障。
請參閱Using Autorelease Pool Blocks。
Use Analysis Tools to Debug Memory Problems - 使用分析工具調(diào)試內(nèi)存問題
為了在編譯時識別代碼的問題卒蘸,可以使用Xcode中內(nèi)置的Clang Static Analyzer
吨枉。如果出現(xiàn)內(nèi)存管理問題圃庭,還可以使用其他工具和技術(shù)來識別和診斷問題帖族。
- 技術(shù)說明
TN2239
,iOS Debugging Magic中的許多工具和技術(shù)都有描述宛渐,特別是使用NSZombie
來幫助找到過度釋放的對象竞漾。 - 您可以使用
Instruments
跟蹤引用計數(shù)事件并查找內(nèi)存泄漏。 請參閱Collecting Data on Your App窥翩。
Memory Management Policy - 內(nèi)存管理策略
用于引用計數(shù)環(huán)境中的內(nèi)存管理的基本模型通過NSObject協(xié)議中定義的方法和標準方法命名約定的組合來提供业岁。 NSObject類還定義了一個方法dealloc
,它在對象被釋放時自動調(diào)用寇蚊。 本文介紹了在Cocoa程序中正確管理內(nèi)存所需的所有基本規(guī)則笔时,并提供了一些正確使用的示例。
1. Basic Memory Management Rules - 基本內(nèi)存管理準則
內(nèi)存管理模型基于對象所有權(quán)幔荒。任何對象可能有一個或多個所有者糊闽。只要一個對象至少有一個所有者梳玫,它就會繼續(xù)存在。如果對象沒有所有者右犹,則運行系統(tǒng)會自動破壞它提澎。要確保什么時候您擁有對象時,什么時候你沒有擁有念链,您可以設(shè)置以下策略:
- 你擁有你創(chuàng)建的任何對象
- 您可以使用名稱以
“alloc”盼忌,“new”,“copy”或“mutableCopy”
(例如alloc掂墓,newObject或mutableCopy)開頭的方法創(chuàng)建對象谦纱。
- 您可以使用名稱以
- 您可以使用
retain
獲取對象的所有權(quán)- 接收到的對象通常被保證在其被接收的方法內(nèi)保持有效,并且該方法也可以安全地將對象返回給其調(diào)用者君编。您可以在兩種情況下使用
retain
:(1)在實現(xiàn)訪問器方法或init
方法時跨嘉,將要存儲為屬性值的對象的所有權(quán);(2)防止對象被無效作為某種其他操作的副作用(如Avoid Causing Deallocation of Objects You’re Using中所述)吃嘿。
- 接收到的對象通常被保證在其被接收的方法內(nèi)保持有效,并且該方法也可以安全地將對象返回給其調(diào)用者君编。您可以在兩種情況下使用
- 當您不再需要它時祠乃,您必須放棄您擁有的對象的所有權(quán)
- 您通過發(fā)送一個release消息或autorelease消息來放棄對象的所有權(quán)。在Cocoa術(shù)語中兑燥,放棄對象的所有權(quán)因此通常被稱為
releasing
對象亮瓷。
- 您通過發(fā)送一個release消息或autorelease消息來放棄對象的所有權(quán)。在Cocoa術(shù)語中兑燥,放棄對象的所有權(quán)因此通常被稱為
- 你不能放棄你不擁有的對象的所有權(quán)
- 這僅僅是以前的政策規(guī)則的推論。
A Simple Example - 一個簡單的例子
為了說明策略降瞳,請考慮以下代碼片段:
{
Person *aPerson = [[Person alloc] init];
// ...
NSString *name = aPerson.fullName;
// ...
[aPerson release];
}
使用alloc
方法創(chuàng)建Person
對象嘱支,因此在不再需要時發(fā)送release
消息。 該person的name不使用任何擁有的方法檢索挣饥,因此不會發(fā)送release消息除师。 請注意,該示例使用release
而不是autorelease
扔枫。
Use autorelease to Send a Deferred release - 使用自動釋放發(fā)送釋放
當您需要發(fā)送延遲release
消息時馍盟,通常在從方法返回對象時使用autorelease
。 例如茧吊,您可以像這樣實現(xiàn)fullName
方法:
- (NSString *)fullName {
NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@",
self.firstName, self.lastName] autorelease];
return string;
}
你擁有由alloc
返回的字符串。 要遵守內(nèi)存管理規(guī)則八毯,您必須先放棄該字符串的所有權(quán)搓侄,然后再丟失該引用。 但是话速,如果您使用release
讶踪,則字符串將在返回之前被釋放(并且該方法將返回無效對象)。 使用autorelease
泊交,您表示您要放棄所有權(quán)乳讥,但您允許該方法的調(diào)用者在釋放之前使用返回的字符串柱查。
你也可以這樣實現(xiàn)fullName
方法:
- (NSString *)fullName {
NSString *string = [NSString stringWithFormat:@"%@ %@",
self.firstName, self.lastName];
return string;
}
遵循基本規(guī)則,您不擁有由stringWithFormat:
返回的字符串云石,因此您可以安全地從方法返回字符串唉工。
作為對比,以下實現(xiàn)是錯誤的:
- (NSString *)fullName {
NSString *string = [[NSString alloc] initWithFormat:@"%@ %@",
self.firstName, self.lastName];
return string;
}
根據(jù)命名約定汹忠,沒有什么可以表示fullName
方法的調(diào)用者擁有返回的字符串淋硝。 因此,調(diào)用者沒有理由釋放返回的字符串宽菜,因此將被泄漏谣膳。
You Don’t Own Objects Returned by Reference - 您沒有擁有引用返回的對象
Cocoa中的一些方法指定通過引用返回一個對象(也就是說,它們接受ClassName **
或id *
類型的參數(shù))铅乡。 一個常見的模式是使用一個NSErro
r對象继谚,其中包含有關(guān)錯誤的信息,如initWithContentsOfURL:options:error:(NSData)
和initWithContentsOfFile:encoding:error:(NSString)
所示阵幸。
在這些情況下花履,相同的規(guī)則適用于已經(jīng)描述的規(guī)則。 當您調(diào)用任何這些方法時侨嘀,您不會創(chuàng)建NSError
對象臭挽,因此您不擁有它。 因此咬腕,無需釋放它欢峰,如本示例所示:
NSString *fileName = <#Get a file name#>;
NSError *error;
NSString *string = [[NSString alloc] initWithContentsOfFile:fileName
encoding:NSUTF8StringEncoding error:&error];
if (string == nil) {
// Deal with error...
}
// ...
[string release];
2. Implement dealloc to Relinquish Ownership of Objects - 實現(xiàn)dealloc放棄對象的所有權(quán)
NSObject類定義了一個方法dealloc,當一個對象沒有所有者涨共,并且它的內(nèi)存被回收時纽帖,它被自動調(diào)用 - 在Cocoa術(shù)語中它被freed
或deallocated
。 dealloc方法的作用是釋放對象自己的內(nèi)存举反,并處理其擁有的任何資源懊直,包括任何對象實例變量的所有權(quán)。
以下示例說明如何為Person類實現(xiàn)dealloc方法:
@interface Person : NSObject
@property (retain) NSString *firstName;
@property (retain) NSString *lastName;
@property (assign, readonly) NSString *fullName;
@end
@implementation Person
// ...
- (void)dealloc
[_firstName release];
[_lastName release];
[super dealloc];
}
@end
重要提示:不要直接調(diào)用另一個對象的dealloc方法火鼻。
在執(zhí)行結(jié)束時室囊,必須調(diào)用超類的實現(xiàn)。
您不應(yīng)將系統(tǒng)資源的管理與對象生命周期相結(jié)合; 請參閱Don’t Use dealloc to Manage Scarce Resources魁索。
當應(yīng)用程序終止時融撞,對象可能不會被發(fā)送一個dealloc消息。 因為進程的內(nèi)存在退出時自動清除粗蔚,所以簡單地說尝偎,允許操作系統(tǒng)清理資源比調(diào)用所有內(nèi)存管理方法更為有效。
3. Core Foundation Uses Similar but Different Rules - Core Foundation使用相似但不同的規(guī)則
Core Foundation對象有類似的內(nèi)存管理規(guī)則(請參閱Memory Management Programming Guide for Core Foundation)。 然而致扯,Cocoa
和Core Foundation
的命名約定是不同的肤寝。 特別是,Core Foundation的創(chuàng)建規(guī)則(請參閱The Create Rule)不適用于返回Objective-C對象的方法抖僵。 例如鲤看,在以下代碼片段中,您不負責放棄myInstance
的所有權(quán):
MyClass *myInstance = [MyClass createInstance];
Practical Memory Management - 實用內(nèi)存管理
雖然Memory Management Policy中描述的基本概念很簡單裆针,但您可以采取一些實際步驟刨摩,使管理內(nèi)存更容易,并幫助確保程序保持可靠和穩(wěn)健世吨,同時最大限度地減少其資源需求澡刹。
1. Use Accessor Methods to Make Memory Management Easier - 使用訪問方法使內(nèi)存管理更輕松
如果您的類具有作為對象的屬性,則必須確保在使用時將設(shè)置為該值的任何對象未被釋放耘婚。 因此罢浇,您必須在設(shè)置對象時聲明對象的所有權(quán)。 您還必須確保您放棄任何當前持有的價值的所有權(quán)沐祷。
有時候看起來很麻煩或迂腐嚷闭,但是如果您一直使用訪問器方法,那么存儲管理問題的機會就會大大降低赖临。 如果您在代碼中使用retain
和release
實例變量胞锰,您幾乎肯定會做錯事。
考慮一個計數(shù)器對象兢榨,其數(shù)目要設(shè)置嗅榕。
@interface Counter : NSObject
@property (nonatomic, retain) NSNumber *count;
@end;
該屬性聲明兩個訪問器方法。 通常吵聪,您應(yīng)該要求編譯器合成方法凌那;然而,看看如何實現(xiàn)它們是有啟發(fā)意義的吟逝。
在get
訪問器中帽蝶,只返回合成的實例變量,因此不需要retain
或release
:
- (NSNumber *)count {
return _count;
}
在set
方法中块攒,如果其他所有人都按照相同的規(guī)則玩励稳,你必須假定新的計數(shù)可能隨時被處置,所以你必須擁有該對象的所有權(quán) - 通過發(fā)送一個retain
消息囱井,以確保它不會被釋放麦锯,您還必須通過發(fā)送release
消息來放棄舊計數(shù)對象的所有權(quán)。 (在Objective-C中允許發(fā)送一個消息到nil琅绅,所以如果_count
還沒有設(shè)置,執(zhí)行將仍然可以工作鹅巍。)千扶,你必須在[newCount retain]
之后發(fā)送料祠,以防兩個是相同對象 - 不想無意中導(dǎo)致它被釋放。
- (void)setCount:(NSNumber *)newCount {
[newCount retain];
[_count release];
// Make the new assignment.
_count = newCount;
}
Use Accessor Methods to Set Property Values - 使用訪問方法設(shè)置屬性值
假設(shè)你想實現(xiàn)一個方法來重置計數(shù)器澎羞。 你有幾個選擇髓绽。 第一個實現(xiàn)使用alloc
創(chuàng)建NSNumber
實例,因此您可以使用release
保持平衡妆绞。
- (void)reset {
NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
[self setCount:zero];
[zero release];
}
第二個使用一個方便的構(gòu)造函數(shù)來創(chuàng)建一個新的NSNumber
對象顺呕。 因此,不需要retain
或release
消息括饶。
- (void)reset {
NSNumber *zero = [NSNumber numberWithInteger:0];
[self setCount:zero];
}
請注意株茶,兩者都使用set accessor
方法。
以下幾乎肯定能夠在簡單的情況下正常工作图焰,但是盡可能避免訪問者的方法启盛,這樣做幾乎肯定在某個階段會導(dǎo)致錯誤(例如,當你忘記retain
或release
技羔,或者如果內(nèi)存管理語義為實例變量改變)僵闯。
- (void)reset {
NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
[_count release];
_count = zero;
}
還要注意,如果您使用key-value observing藤滥,則以這種方式更改變量不符合KVO鳖粟。
Don’t Use Accessor Methods in Initializer Methods and dealloc - 不要在初始化程序方法和dealloc中使用Accessor方法
唯一不應(yīng)該使用訪問器方法來設(shè)置實例變量的地方在initializer
方法和dealloc
中。 要初始化一個counter
對象為零的計數(shù)器對象拙绊,可以按如下方式實現(xiàn)init
方法:
- init {
self = [super init];
if (self) {
_count = [[NSNumber alloc] initWithInteger:0];
}
return self;
}
為了允許使用不為零的counter
初始化計數(shù)器向图,可以按如下方式實現(xiàn)initWithCount:
方法:
- initWithCount:(NSNumber *)startingCount {
self = [super init];
if (self) {
_count = [startingCount copy];
}
return self;
}
由于Counter
類有一個對象實例變量,所以你必須實現(xiàn)一個dealloc
方法时呀。 它應(yīng)該放棄任何實例變量的所有權(quán)张漂,通過發(fā)送一個release
消息,最終應(yīng)該調(diào)用super的實現(xiàn):
- (void)dealloc {
[_count release];
[super dealloc];
}
2. Use Weak References to Avoid Retain Cycles - 使用弱引用避免引用循環(huán)
保留對象將創(chuàng)建對該對象的強引用谨娜。 一個對象在其所有強引用被釋放之前都不能被釋放航攒。 因此,如果兩個對象可能具有循環(huán)引用趴梢,就會產(chǎn)生一個稱為retain cycle
的問題 - 即它們彼此有很強的引用(直接地或者通過其他對象的鏈漠畜,每一個都有強烈的引用到下一個,又與第一個形成閉環(huán))坞靶。
Figure 1所示的對象關(guān)系說明了潛在的retain cycle
憔狞。 Document
對象對于文檔中的每個頁面都有一個Page
對象。 每個Page
對象都有一個屬性彰阴,可以跟蹤它所在的文檔瘾敢。如果Document對象具有對Page對象的強烈引用,并且Page對象具有對Document對象的強烈引用,那么這兩個對象都不能被釋放簇抵。 在Document對象被釋放之前庆杜,Document的引用計數(shù)不能變?yōu)榱悖⑶以卺尫臘ocument對象之前不會釋放Page對象碟摆。
retain cycles
問題的解決方案是使用弱引用晃财。弱引用是不擁有的關(guān)系,其中源對象不保留其具有引用的對象典蜕。
然而断盛,為了保持對象圖形的完整性,在某處必須有很強的引用(如果只有弱引用愉舔,那么頁面和段落可能沒有任何所有者钢猛,因此將被釋放)。Cocoa
可以建立一個公約屑宠,因此厢洞,一個parent
對象應(yīng)該保持對children
的強引用,而且孩子們對他們的父母類應(yīng)該保持弱引用典奉。
因此躺翻,在Figure 1中,文檔對象document
具有強引用(保留)其Page
對象卫玖,但是Page
對象具有對(不保留)文檔對象的弱引用公你。
Cocoa中弱引用的示例包括但不限于表數(shù)據(jù)源,table data sources
, outline view items
, notification observers, 和 miscellaneous targets
和 delegates假瞬。
您需要十分小心向僅保存弱引用的對象發(fā)送消息陕靠。如果您在釋放對象后發(fā)送消息,您的應(yīng)用程序?qū)⒈罎⑼衍浴.攲ο笥行r剪芥,您必須具有明確的條件。在大多數(shù)情況下琴许,弱引用的對象知道另一個對象的弱引用税肪,如循環(huán)引用的情況,并且負責在釋放另一個對象時進行通知榜田。例如益兄,當您在通知中心注冊對象時,通知中心存儲對該對象的弱引用箭券,并在發(fā)布相應(yīng)的通知時向其發(fā)送消息净捅。當對象被釋放時,您需要使用通知中心取消注冊辩块,以防止通知中心向?qū)ο蟀l(fā)送任何不再存在的消息蛔六。同樣荆永,當代理對象被釋放時,您需要通過向另一個對象發(fā)送一個帶有nil
參數(shù)的setDelegate:
消息來刪除委托鏈接国章。這些消息通常是從對象的dealloc
方法發(fā)送的邻耕。
3. Avoid Causing Deallocation of Objects You’re Using - 避免導(dǎo)致您正在使用的對象釋放
Cocoa的所有權(quán)策略指定接收的對象通常在調(diào)用方法的整個范圍內(nèi)保持有效氨淌。 也可以從當前范圍返回接收到的對象剩岳,而不用擔心它被釋放秋冰。 對應(yīng)用程序來說廷没,對象的getter
方法返回一個緩存的實例變量或一個計算的值是不重要的盗蟆。 重要的是虫埂,該對象在您需要時保持有效贷盲。
這個規(guī)則偶爾有例外坏匪,主要分為兩類拟逮。
- 當一個對象從基礎(chǔ)collection classes中移除時。
heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// heisenObject could now be invalid.
當一個對象從一個基本的集合類中刪除時适滓,它被發(fā)送一個release
(而不是autorelease
)消息敦迄。 如果集合是已刪除對象的唯一所有者,則會立即釋放已刪除的對象(示例中的heisenObject
)凭迹。
- 當
parent object
釋放時
id parent = <#create a parent object#>;
// ...
heisenObject = [parent child] ;
[parent release]; // Or, for example: self.parent = nil;
// heisenObject could now be invalid.
在某些情況下罚屋,您從另一個對象中檢索對象,然后直接或間接釋放父對象嗅绸。 如果釋放父項導(dǎo)致它被釋放脾猛,并且父對象是該子節(jié)點的唯一所有者,那么該子節(jié)點(該示例中的heisenObject
)將同時被釋放(假設(shè)發(fā)送一個release
而不是一個autorelease
消息在父的dealloc
方法中)鱼鸠。
為了防止這些情況猛拴,您在收到heisenObject
后,保留heisenObject蚀狰,并在完成之后釋放它愉昆。 例如:
heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// Use heisenObject...
[heisenObject release];
4. Don’t Use dealloc to Manage Scarce Resources - 不要使用dealloc來管理稀缺資源
在dealloc方法中,通常不應(yīng)該管理諸如文件描述符麻蹋,網(wǎng)絡(luò)連接跛溉,緩沖區(qū)或緩存等稀缺資源。特別是哥蔚,您不應(yīng)該設(shè)計類倒谷,以便在您認為它將被調(diào)用時調(diào)用dealloc。調(diào)用dealloc可能會因為錯誤或應(yīng)用程序崩潰而被延遲或回避糙箍。
相反渤愁,如果您有一個實例管理稀缺資源的類,則應(yīng)該設(shè)計應(yīng)用程序深夯,以便您知道何時不再需要資源抖格,然后可以告訴實例clean up
該點诺苹。你通常會釋放這個實例,而dealloc
會遵循雹拄,但是如果沒有的話收奔,你不會遭受額外的問題。
如果您嘗試在dealloc
之上捎帶資源管理滓玖,可能會出現(xiàn)問題坪哄。例如:
-
object graph 拆卸的順序依賴。
- 對象圖拆除機制本質(zhì)上是無序的势篡。雖然您通臭婕。可能希望獲得特定的順序,但您正在引入脆弱性禁悠。如果一個對象意外地自動釋放而不是被釋放念祭,例如,拆除順序可能會改變碍侦,這可能會導(dǎo)致意想不到的結(jié)果粱坤。
- 不填補稀缺資源。
- 內(nèi)存泄漏是應(yīng)該修復(fù)的錯誤瓷产,但它們通常不會立即死機站玄。但是,如果您希望稀缺的資源釋放但是他們卻不釋放時拦英,那么您可能遇到更嚴重的問題蜒什。例如,如果您的應(yīng)用程序用盡了文件描述符疤估,則用戶可能無法保存數(shù)據(jù)灾常。
- 清除正在錯誤的線程上執(zhí)行的邏輯。
- 如果一個對象在一個意外的時間被自動釋放铃拇,它將被釋放在任何線程的自動釋放池塊钞瀑,它恰好在其中。對于只應(yīng)該從一個線程獲取的資源慷荔,這可能很容易致命雕什。
5. Collections Own the Objects They Contain - 集合擁有它們包含的對象
當您將對象添加到集合(如數(shù)組,字典或集合)時显晶,集合將擁有該集合的所有權(quán)贷岸。 當對象從集合中移除或集合本身被釋放時,集合將放棄所有權(quán)磷雇。 因此偿警,例如,如果要創(chuàng)建數(shù)字數(shù)組唯笙,則可以執(zhí)行以下操作之一:
NSMutableArray *array = <#Get a mutable array#>;
NSUInteger i;
// ...
for (i = 0; i < 10; i++) {
NSNumber *convenienceNumber = [NSNumber numberWithInteger:i];
[array addObject:convenienceNumber];
}
在這種情況下螟蒸,您沒有調(diào)用alloc
盒使,所以不需要調(diào)用release
。 沒有必要retain
新數(shù)字(convenienceNumber)
七嫌,因為數(shù)組將這樣做少办。
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];
}
在這種情況下,您需要在for循環(huán)的范圍內(nèi)發(fā)送allocedNumber
釋放消息以平衡alloc
诵原。 由于數(shù)組在addObject
添加時retain number
英妓,所以在數(shù)組中不會釋放它。
要理解這一點绍赛,把自己置于執(zhí)行收集類的person
的位置鞋拟。 你想確保沒有給你的對象從你的下面消失,所以你傳送給他們retain
消息惹资,如果它們被刪除,你必須發(fā)送一個平衡release
消息航闺, 并且任何存在的對象應(yīng)在您自己的dealloc方法期間發(fā)送release
消息褪测。
6. Ownership Policy Is Implemented Using Retain Counts - 使用Retain Counts實現(xiàn)所有權(quán)策略
所有權(quán)政策通過引用計數(shù)來實現(xiàn),通常在retain
方法之后稱為retain count
潦刃。 每個對象都有一個保留計數(shù)侮措。
- 創(chuàng)建對象時,它的保留計數(shù)為1乖杠。
- 當您向?qū)ο蟀l(fā)送
retain
消息時分扎,其保留計數(shù)將遞增1。 - 當您向?qū)ο蟀l(fā)送
release
消息時胧洒,其保留計數(shù)將遞減1畏吓。 - 當您向?qū)ο蟀l(fā)送
autorelease
消息時,其引用計數(shù)在當前自動釋放池塊的末尾遞減1卫漫。 - 如果對象的引用計數(shù)減少為零菲饼,則將其釋放。
重要:應(yīng)該沒有不需要明確的說明一個對象它的保留計數(shù)是什么(參見retainCount)列赎。 結(jié)果通常是誤導(dǎo)性的宏悦,因為您可能不知道框架對象是否保留了您感興趣的對象。 在調(diào)試內(nèi)存管理問題時包吝,您應(yīng)該注意確保代碼遵守所有權(quán)規(guī)則饼煞。
Using Autorelease Pool Blocks - 使用自動釋放池塊
自動釋放池塊提供了一種機制,您可以放棄對象的所有權(quán)诗越,但避免立即釋放它的可能性(例如從方法返回對象時)砖瞧。 通常,您不需要創(chuàng)建自己的自動釋放池塊掺喻,但是有一些情況芭届,您必須或有必要這樣做储矩。
1. About Autorelease Pool Blocks - 關(guān)于自動釋放池塊
使用@autoreleasepool
標記自動釋放池塊,如以下示例所示:
@autoreleasepool {
// Code that creates autoreleased objects.
}
在自動釋放池塊的末尾褂乍,在塊內(nèi)接收到自動釋放消息的對象被發(fā)送一個release
消息 - 對象每次在塊內(nèi)發(fā)送一個autorelease
消息時都會收到一個release
消息持隧。
像任何其他代碼塊一樣,自動釋放池塊可以嵌套:
@autoreleasepool {
// . . .
@autoreleasepool {
// . . .
}
. . .
}
(通常您通常不會像上面那樣看到代碼逃片,通常屡拨,一個源文件中的自動釋放池塊中的代碼將調(diào)用另一個自動釋放池塊中包含的另一個源文件中的代碼。)對于給定的autorelease
消息褥实,相應(yīng)的release
消息在自動釋放池塊的末尾發(fā)送autorelease
消息時被發(fā)送呀狼。
Cocoa總是期望在自動釋放池塊中執(zhí)行代碼,否則自動釋放的對象不會被釋放损离,并且應(yīng)用程序泄漏內(nèi)存哥艇。 (如果您在自動釋放池塊之外發(fā)送autorelease
消息,Cocoa會記錄一個合適的錯誤消息僻澎。)AppKit
和UIKit
框架處理自動釋放池塊中的每個事件循環(huán)迭代(例如鼠標向下事件或點擊)貌踏。因此,您通常不必自己創(chuàng)建自動釋放池塊窟勃,甚至可以看到用于創(chuàng)建自動釋放池塊的代碼祖乳。但是,有三種情況可能會使用您自己的autorelease
池塊:
- 如果您正在編寫一個不基于UI框架的程序秉氧,例如命令行工具眷昆。
- 如果你寫一個創(chuàng)建許多臨時對象的循環(huán)。
- 您可以在循環(huán)中使用自動釋放池塊在下一次迭代之前處理這些對象汁咏。在循環(huán)中使用自動釋放池塊有助于減少應(yīng)用程序的最大內(nèi)存占用亚斋。
- 如果你產(chǎn)生一個二級線程。
- 一旦線程開始執(zhí)行攘滩,您必須創(chuàng)建自己的自動釋放池塊伞访;否則,您的應(yīng)用程序?qū)⑿孤ο蟆?(有關(guān)詳細信息轰驳,請參閱Autorelease Pool Blocks and Threads 厚掷。)
2. Use Local Autorelease Pool Blocks to Reduce Peak Memory Footprint - 使用本地自動釋放池塊來減少峰值內(nèi)存占用
許多程序創(chuàng)建自動釋放的臨時對象。 這些對象增加了程序的內(nèi)存占用空間级解,直到塊的結(jié)尾冒黑。 在許多情況下,允許臨時對象累積直到當前事件循環(huán)迭代結(jié)束不會導(dǎo)致過多的開銷勤哗;但是在某些情況下抡爹,您可能會創(chuàng)建大量臨時對象,從而大大增加內(nèi)存占用空間芒划,并希望更快地處理它們冬竟。 在后一種情況下欧穴,您可以創(chuàng)建自己的自動釋放池塊。 在塊的末尾泵殴,臨時對象被釋放涮帘,這通常導(dǎo)致其釋放,從而減少程序的內(nèi)存占用笑诅。
以下示例顯示如何在for循環(huán)中使用本地自動釋放池塊调缨。
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) {
@autoreleasepool {
NSError *error;
NSString *fileContents = [NSString stringWithContentsOfURL:url
encoding:NSUTF8StringEncoding error:&error];
/* Process the string, creating and autoreleasing more objects. */
}
}
for循環(huán)一次處理一個文件。 在自動釋放池塊中發(fā)送autorelease
消息的任何對象(如fileContents)在塊的末尾釋放吆你。
在自動釋放池塊之后弦叶,您應(yīng)該將塊內(nèi)自動釋放的任何對象視為disposed of
。不要向該對象發(fā)送消息或?qū)⑵浞祷亟o方法的調(diào)用者妇多。 如果您必須使用超出自動釋放池塊的臨時對象伤哺,則可以通過向塊中的對象發(fā)送retain
消息,然后在塊之后發(fā)送autorelease
者祖,如本示例所示:
– (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. */
}
在autorelease pool
塊中發(fā)送retain
去match
默责,并在自動釋放池塊擴展match
的生命周期后發(fā)送autorelease
,并允許它在循環(huán)之外接收消息咸包,并返回給findMatchingObject:
的調(diào)用者。
3. Autorelease Pool Blocks and Threads - 自動釋放池塊和線程
Cocoa應(yīng)用程序中的每個線程都維護自己的堆棧自動釋放池塊杖虾。如果您正在編寫僅基礎(chǔ)程序或分離線程烂瘫,則需要創(chuàng)建自己的自動釋放池塊。
如果您的應(yīng)用程序或線程長期存在奇适,并且可能會生成大量自動釋放的對象坟比,則應(yīng)使用自動釋放池塊(如主線程上的AppKit和UIKit);否則嚷往,自動釋放的對象會累積葛账,并且您的內(nèi)存占用將增長。如果您的分離線程不會使Cocoa調(diào)用皮仁,則不需要使用自動釋放池塊籍琳。
注意:如果使用POSIX
線程API而不是NSThread
創(chuàng)建輔助線程,則除非Cocoa處于多線程模式贷祈,否則不能使用Cocoa趋急。 Cocoa僅在分離其第一個NSThread
對象后才進入多線程模式。要在輔助POSIX線程上使用Cocoa势誊,您的應(yīng)用程序必須首先分離至少一個可以立即退出的NSThread對象呜达。您可以使用NSThread
類方法isMultiThreaded來測試Cocoa是否處于多線程模式。
后記
未完粟耻,待續(xù)~~~