CoreFoundation框架詳細解析(五) —— 高級內(nèi)存管理(一)

版本記錄

版本號 時間
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ù)說明TN2239iOS 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掂墓,newObjectmutableCopy)開頭的方法創(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中所述)吃嘿。
  • 當您不再需要它時祠乃,您必須放棄您擁有的對象的所有權(quán)
    • 您通過發(fā)送一個release消息或autorelease消息來放棄對象的所有權(quán)。在Cocoa術(shù)語中兑燥,放棄對象的所有權(quán)因此通常被稱為releasing對象亮瓷。
  • 你不能放棄你不擁有的對象的所有權(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ù))铅乡。 一個常見的模式是使用一個NSError對象继谚,其中包含有關(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ù)語中它被freeddeallocated。 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)。 然而致扯,CocoaCore 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)沐祷。

有時候看起來很麻煩或迂腐嚷闭,但是如果您一直使用訪問器方法,那么存儲管理問題的機會就會大大降低赖临。 如果您在代碼中使用retainrelease實例變量胞锰,您幾乎肯定會做錯事。

考慮一個計數(shù)器對象兢榨,其數(shù)目要設(shè)置嗅榕。

@interface Counter : NSObject
@property (nonatomic, retain) NSNumber *count;
@end;

該屬性聲明兩個訪問器方法。 通常吵聪,您應(yīng)該要求編譯器合成方法凌那;然而,看看如何實現(xiàn)它們是有啟發(fā)意義的吟逝。

get訪問器中帽蝶,只返回合成的實例變量,因此不需要retainrelease

- (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對象顺呕。 因此,不需要retainrelease消息括饶。

- (void)reset {
    NSNumber *zero = [NSNumber numberWithInteger:0];
    [self setCount:zero];
}

請注意株茶,兩者都使用set accessor方法。

以下幾乎肯定能夠在簡單的情況下正常工作图焰,但是盡可能避免訪問者的方法启盛,這樣做幾乎肯定在某個階段會導(dǎo)致錯誤(例如,當你忘記retainrelease技羔,或者如果內(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對象碟摆。

Figure 1 An illustration of cyclical references

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 targetsdelegates假瞬。

您需要十分小心向僅保存弱引用的對象發(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ī)則偶爾有例外坏匪,主要分為兩類拟逮。

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會記錄一個合適的錯誤消息僻澎。)AppKitUIKit框架處理自動釋放池塊中的每個事件循環(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ā)送retainmatch默责,并在自動釋放池塊擴展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ù)~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末查近,一起剝皮案震驚了整個濱河市眉踱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌霜威,老刑警劉巖谈喳,帶你破解...
    沈念sama閱讀 212,080評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異侥祭,居然都是意外死亡叁执,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評論 3 385
  • 文/潘曉璐 我一進店門矮冬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谈宛,“玉大人,你說我怎么就攤上這事胎署∵郝迹” “怎么了?”我有些...
    開封第一講書人閱讀 157,630評論 0 348
  • 文/不壞的土叔 我叫張陵琼牧,是天一觀的道長恢筝。 經(jīng)常有香客問我,道長巨坊,這世上最難降的妖魔是什么撬槽? 我笑而不...
    開封第一講書人閱讀 56,554評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮趾撵,結(jié)果婚禮上侄柔,老公的妹妹穿的比我還像新娘。我一直安慰自己占调,他們只是感情好暂题,可當我...
    茶點故事閱讀 65,662評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著究珊,像睡著了一般薪者。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上剿涮,一...
    開封第一講書人閱讀 49,856評論 1 290
  • 那天言津,我揣著相機與錄音,去河邊找鬼取试。 笑死纺念,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的想括。 我是一名探鬼主播陷谱,決...
    沈念sama閱讀 39,014評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了烟逊?” 一聲冷哼從身側(cè)響起渣窜,我...
    開封第一講書人閱讀 37,752評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宪躯,沒想到半個月后乔宿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,212評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡访雪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,541評論 2 327
  • 正文 我和宋清朗相戀三年详瑞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臣缀。...
    茶點故事閱讀 38,687評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡坝橡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出精置,到底是詐尸還是另有隱情计寇,我是刑警寧澤,帶...
    沈念sama閱讀 34,347評論 4 331
  • 正文 年R本政府宣布脂倦,位于F島的核電站番宁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏赖阻。R本人自食惡果不足惜蝶押,卻給世界環(huán)境...
    茶點故事閱讀 39,973評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望火欧。 院中可真熱鬧棋电,春花似錦、人聲如沸布隔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衅檀。三九已至,卻和暖如春霎俩,著一層夾襖步出監(jiān)牢的瞬間哀军,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評論 1 266
  • 我被黑心中介騙來泰國打工打却, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留杉适,地道東北人。 一個月前我還...
    沈念sama閱讀 46,406評論 2 360
  • 正文 我出身青樓柳击,卻偏偏與公主長得像猿推,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,576評論 2 349

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

  • 1.簡介 應(yīng)用程序的內(nèi)存管理就是在程序的運行期開辟內(nèi)存蹬叭,使用內(nèi)存藕咏,使用完畢后釋放內(nèi)存。一個好的程序使用盡可能少的內(nèi)...
    BoomLee閱讀 1,958評論 8 7
  • 內(nèi)存管理是程序在運行時分配內(nèi)存秽五、使用內(nèi)存孽查,并在程序完成時釋放內(nèi)存的過程。在Objective-C中坦喘,也被看作是在眾...
    蹲瓜閱讀 3,035評論 1 8
  • 1. 內(nèi)總管理原則(引用計數(shù)) IOS的對象都繼承于NSObject, 該對象有一個方法:retainCount...
    lilinjianshu閱讀 2,152評論 0 2
  • 29.理解引用計數(shù) Objective-C語言使用引用計數(shù)來管理內(nèi)存盲再,也就是說,每個對象都有個可以遞增或遞減的計數(shù)...
    Code_Ninja閱讀 1,477評論 1 3
  • 我有個朋友瓣铣,她的朋友是一個矮萌妹子答朋,叫相相。今天我們就來說說這個相相的故事坯沪。 看人嘛绿映,首先是長相,我認識她的時候腐晾,...
    北辰北閱讀 408評論 0 1