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

版本記錄

版本號 時間
V1.0 2017.10.07

前言

Core Foundation框架(CoreFoundation.framework)是一組C語言接口,它們?yōu)閕OS應用程序提供基本數(shù)據管理和服務功能。接下來我們就詳細的解析這個框架。感興趣的可以看我上面寫的幾篇。
1. CoreFoundation框架詳細解析(一) —— 基本概覽
2. CoreFoundation框架詳細解析(二) —— 設計概念
3. CoreFoundation框架詳細解析(三) —— 內存管理(一)
4. CoreFoundation框架詳細解析(四) —— 內存管理(二)

About Memory Management - 關于內存管理

應用程序內存管理是在程序運行時分配內存的過程,使用它,并在完成后釋放內存。 一個寫得好的程序盡可能少地使用內存。 在Objective-C中,它也可以被看作是在許多數(shù)據和代碼之間分配有限的內存資源的所有權的一種方式。 完成本指南后,通過明確管理對象的生命周期并在不再需要時釋放它們,您將擁有管理應用程序內存所需的知識。

雖然通常將內存管理視為單個對象的級別,但您的目標實際上是管理object graphs.。 你想要確保你在內存中沒有比實際需要更多的對象。

1. At a Glance - 概覽

Objective-C提供了兩種應用程序內存管理方法。

  • 在本指南中描述的方法(稱為manual retain-release)或MRR中,通過跟蹤您擁有的對象來明確管理內存。 這是使用基礎類NSObject與運行時環(huán)境結合提供的模型(稱為引用計數(shù))來實現(xiàn)的。
  • 在自動引用計數(shù)或ARC中,系統(tǒng)使用與MRR相同的引用計數(shù)系統(tǒng),但在編譯時會為您插入適當?shù)膬却婀芾矸椒ㄕ{用。 強烈建議您使用ARC進行新項目。 如果您使用ARC,通常不需要了解本文檔中描述的基礎實現(xiàn),盡管在某些情況下可能會有所幫助。 有關ARC的更多信息,請參閱Transitioning to ARC Release Notes。

Good Practices Prevent Memory-Related Problems - 良好做法防止與內存相關的問題

內存管理不正確導致的主要問題有兩種:

  • 釋放或重寫仍在使用的數(shù)據
    • 這會導致內存損壞,并且通常會導致應用程序崩潰,甚至導致用戶數(shù)據損壞。
  • 不釋放不再使用的數(shù)據會導致內存泄漏
    • 內存泄漏是分配的內存不被釋放的地方,盡管它再也不會被使用。泄漏導致您的應用程序使用不斷增加的內存量,這反過來可能導致系統(tǒng)性能較差或您的應用程序被終止。

從引用計數(shù)的角度考慮內存管理往往適得其反,然而,因為您傾向于根據實施細節(jié)而不是實際目標考慮內存管理。相反,您應該從對象所有權和 object graphs的角度考慮內存管理。

Cocoa使用一個簡單的命名約定來表示何時擁有一個方法返回的對象。

請參閱Memory Management Policy。

盡管基本策略是直截了當?shù)?,但您可以采取一些實際步驟,使管理內存更容易,并幫助確保程序保持可靠和穩(wěn)健,同時最大限度地減少其資源需求。

請參閱Practical Memory Management。

自動釋放池塊提供了一種機制,您可以向對象發(fā)送deferred release發(fā)布消息。這在您想要放棄對象的所有權的情況下很有用,但是希望避免立即釋放它的可能性(例如從方法返回對象時)。有時您可能會使用自己的autorelease池塊。

請參閱Using Autorelease Pool Blocks

Use Analysis Tools to Debug Memory Problems - 使用分析工具調試內存問題

為了在編譯時識別代碼的問題,可以使用Xcode中內置的Clang Static Analyzer。如果出現(xiàn)內存管理問題,還可以使用其他工具和技術來識別和診斷問題。

  • 技術說明TN2239,iOS Debugging Magic中的許多工具和技術都有描述,特別是使用NSZombie來幫助找到過度釋放的對象。
  • 您可以使用Instruments跟蹤引用計數(shù)事件并查找內存泄漏。 請參閱Collecting Data on Your App。

Memory Management Policy - 內存管理策略

用于引用計數(shù)環(huán)境中的內存管理的基本模型通過NSObject協(xié)議中定義的方法和標準方法命名約定的組合來提供。 NSObject類還定義了一個方法dealloc,它在對象被釋放時自動調用。 本文介紹了在Cocoa程序中正確管理內存所需的所有基本規(guī)則,并提供了一些正確使用的示例。

1. Basic Memory Management Rules - 基本內存管理準則

內存管理模型基于對象所有權。任何對象可能有一個或多個所有者。只要一個對象至少有一個所有者,它就會繼續(xù)存在。如果對象沒有所有者,則運行系統(tǒng)會自動破壞它。要確保什么時候您擁有對象時,什么時候你沒有擁有,您可以設置以下策略:

  • 你擁有你創(chuàng)建的任何對象
    • 您可以使用名稱以“alloc”,“new”,“copy”或“mutableCopy”(例如alloc,newObjectmutableCopy)開頭的方法創(chuàng)建對象。
  • 您可以使用retain獲取對象的所有權
    • 接收到的對象通常被保證在其被接收的方法內保持有效,并且該方法也可以安全地將對象返回給其調用者。您可以在兩種情況下使用retain:(1)在實現(xiàn)訪問器方法或init方法時,將要存儲為屬性值的對象的所有權;(2)防止對象被無效作為某種其他操作的副作用(如Avoid Causing Deallocation of Objects You’re Using中所述)。
  • 當您不再需要它時,您必須放棄您擁有的對象的所有權
    • 您通過發(fā)送一個release消息或autorelease消息來放棄對象的所有權。在Cocoa術語中,放棄對象的所有權因此通常被稱為releasing對象。
  • 你不能放棄你不擁有的對象的所有權
    • 這僅僅是以前的政策規(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返回的字符串。 要遵守內存管理規(guī)則,您必須先放棄該字符串的所有權,然后再丟失該引用。 但是,如果您使用release,則字符串將在返回之前被釋放(并且該方法將返回無效對象)。 使用autorelease,您表示您要放棄所有權,但您允許該方法的調用者在釋放之前使用返回的字符串。

你也可以這樣實現(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;
}

根據命名約定,沒有什么可以表示fullName方法的調用者擁有返回的字符串。 因此,調用者沒有理由釋放返回的字符串,因此將被泄漏。

You Don’t Own Objects Returned by Reference - 您沒有擁有引用返回的對象

Cocoa中的一些方法指定通過引用返回一個對象(也就是說,它們接受ClassName **id *類型的參數(shù))。 一個常見的模式是使用一個NSError對象,其中包含有關錯誤的信息,如initWithContentsOfURL:options:error:(NSData)initWithContentsOfFile:encoding:error:(NSString)所示。

在這些情況下,相同的規(guī)則適用于已經描述的規(guī)則。 當您調用任何這些方法時,您不會創(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放棄對象的所有權

NSObject類定義了一個方法dealloc,當一個對象沒有所有者,并且它的內存被回收時,它被自動調用 - 在Cocoa術語中它被freeddeallocated。 dealloc方法的作用是釋放對象自己的內存,并處理其擁有的任何資源,包括任何對象實例變量的所有權。

以下示例說明如何為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

重要提示:不要直接調用另一個對象的dealloc方法。
在執(zhí)行結束時,必須調用超類的實現(xiàn)。
您不應將系統(tǒng)資源的管理與對象生命周期相結合; 請參閱Don’t Use dealloc to Manage Scarce Resources。
當應用程序終止時,對象可能不會被發(fā)送一個dealloc消息。 因為進程的內存在退出時自動清除,所以簡單地說,允許操作系統(tǒng)清理資源比調用所有內存管理方法更為有效。

3. Core Foundation Uses Similar but Different Rules - Core Foundation使用相似但不同的規(guī)則

Core Foundation對象有類似的內存管理規(guī)則(請參閱Memory Management Programming Guide for Core Foundation)。 然而,CocoaCore Foundation的命名約定是不同的。 特別是,Core Foundation的創(chuàng)建規(guī)則(請參閱The Create Rule)不適用于返回Objective-C對象的方法。 例如,在以下代碼片段中,您不負責放棄myInstance的所有權:

MyClass *myInstance = [MyClass createInstance];

Practical Memory Management - 實用內存管理

雖然Memory Management Policy中描述的基本概念很簡單,但您可以采取一些實際步驟,使管理內存更容易,并幫助確保程序保持可靠和穩(wěn)健,同時最大限度地減少其資源需求。

1. Use Accessor Methods to Make Memory Management Easier - 使用訪問方法使內存管理更輕松

如果您的類具有作為對象的屬性,則必須確保在使用時將設置為該值的任何對象未被釋放。 因此,您必須在設置對象時聲明對象的所有權。 您還必須確保您放棄任何當前持有的價值的所有權。

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

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

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

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

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

- (NSNumber *)count {
    return _count;
}

set方法中,如果其他所有人都按照相同的規(guī)則玩,你必須假定新的計數(shù)可能隨時被處置,所以你必須擁有該對象的所有權 - 通過發(fā)送一個retain消息,以確保它不會被釋放,您還必須通過發(fā)送release消息來放棄舊計數(shù)對象的所有權。 (在Objective-C中允許發(fā)送一個消息到nil,所以如果_count還沒有設置,執(zhí)行將仍然可以工作。),你必須在[newCount retain]之后發(fā)送,以防兩個是相同對象 - 不想無意中導致它被釋放。

- (void)setCount:(NSNumber *)newCount {

[newCount retain];

[_count release];

// Make the new assignment.

_count = newCount;

}

Use Accessor Methods to Set Property Values - 使用訪問方法設置屬性值

假設你想實現(xiàn)一個方法來重置計數(shù)器。 你有幾個選擇。 第一個實現(xiàn)使用alloc創(chuàng)建NSNumber實例,因此您可以使用release保持平衡。

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

第二個使用一個方便的構造函數(shù)來創(chuàng)建一個新的NSNumber對象。 因此,不需要retainrelease消息。

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

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

以下幾乎肯定能夠在簡單的情況下正常工作,但是盡可能避免訪問者的方法,這樣做幾乎肯定在某個階段會導致錯誤(例如,當你忘記retainrelease,或者如果內存管理語義為實例變量改變)。

- (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方法

唯一不應該使用訪問器方法來設置實例變量的地方在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方法。 它應該放棄任何實例變量的所有權,通過發(fā)送一個release消息,最終應該調用super的實現(xiàn):

- (void)dealloc {

[_count release];

[super dealloc];

}

2. Use Weak References to Avoid Retain Cycles - 使用弱引用避免引用循環(huán)

保留對象將創(chuàng)建對該對象的強引用。 一個對象在其所有強引用被釋放之前都不能被釋放。 因此,如果兩個對象可能具有循環(huán)引用,就會產生一個稱為retain cycle的問題 - 即它們彼此有很強的引用(直接地或者通過其他對象的鏈,每一個都有強烈的引用到下一個,又與第一個形成閉環(huán))。

Figure 1所示的對象關系說明了潛在的retain cycle。 Document對象對于文檔中的每個頁面都有一個Page對象。 每個Page對象都有一個屬性,可以跟蹤它所在的文檔。如果Document對象具有對Page對象的強烈引用,并且Page對象具有對Document對象的強烈引用,那么這兩個對象都不能被釋放。 在Document對象被釋放之前,Document的引用計數(shù)不能變?yōu)榱?,并且在釋放Document對象之前不會釋放Page對象。

Figure 1 An illustration of cyclical references

retain cycles問題的解決方案是使用弱引用。弱引用是不擁有的關系,其中源對象不保留其具有引用的對象。

然而,為了保持對象圖形的完整性,在某處必須有很強的引用(如果只有弱引用,那么頁面和段落可能沒有任何所有者,因此將被釋放)。Cocoa可以建立一個公約,因此,一個parent對象應該保持對children的強引用,而且孩子們對他們的父母類應該保持弱引用。

因此,在Figure 1中,文檔對象document具有強引用(保留)其Page對象,但是Page對象具有對(不保留)文檔對象的弱引用。

Cocoa中弱引用的示例包括但不限于表數(shù)據源,table data sources, outline view items, notification observers, 和 miscellaneous targetsdelegates。

您需要十分小心向僅保存弱引用的對象發(fā)送消息。如果您在釋放對象后發(fā)送消息,您的應用程序將崩潰。當對象有效時,您必須具有明確的條件。在大多數(shù)情況下,弱引用的對象知道另一個對象的弱引用,如循環(huán)引用的情況,并且負責在釋放另一個對象時進行通知。例如,當您在通知中心注冊對象時,通知中心存儲對該對象的弱引用,并在發(fā)布相應的通知時向其發(fā)送消息。當對象被釋放時,您需要使用通知中心取消注冊,以防止通知中心向對象發(fā)送任何不再存在的消息。同樣,當代理對象被釋放時,您需要通過向另一個對象發(fā)送一個帶有nil參數(shù)的setDelegate:消息來刪除委托鏈接。這些消息通常是從對象的dealloc方法發(fā)送的。

3. Avoid Causing Deallocation of Objects You’re Using - 避免導致您正在使用的對象釋放

Cocoa的所有權策略指定接收的對象通常在調用方法的整個范圍內保持有效。 也可以從當前范圍返回接收到的對象,而不用擔心它被釋放。 對應用程序來說,對象的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.

在某些情況下,您從另一個對象中檢索對象,然后直接或間接釋放父對象。 如果釋放父項導致它被釋放,并且父對象是該子節(jié)點的唯一所有者,那么該子節(jié)點(該示例中的heisenObject)將同時被釋放(假設發(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方法中,通常不應該管理諸如文件描述符,網絡連接,緩沖區(qū)或緩存等稀缺資源。特別是,您不應該設計類,以便在您認為它將被調用時調用dealloc。調用dealloc可能會因為錯誤或應用程序崩潰而被延遲或回避。

相反,如果您有一個實例管理稀缺資源的類,則應該設計應用程序,以便您知道何時不再需要資源,然后可以告訴實例clean up該點。你通常會釋放這個實例,而dealloc會遵循,但是如果沒有的話,你不會遭受額外的問題。

如果您嘗試在dealloc之上捎帶資源管理,可能會出現(xiàn)問題。例如:

  • object graph 拆卸的順序依賴。
    • 對象圖拆除機制本質上是無序的。雖然您通常可能希望獲得特定的順序,但您正在引入脆弱性。如果一個對象意外地自動釋放而不是被釋放,例如,拆除順序可能會改變,這可能會導致意想不到的結果。
  • 不填補稀缺資源。
    • 內存泄漏是應該修復的錯誤,但它們通常不會立即死機。但是,如果您希望稀缺的資源釋放但是他們卻不釋放時,那么您可能遇到更嚴重的問題。例如,如果您的應用程序用盡了文件描述符,則用戶可能無法保存數(shù)據。
  • 清除正在錯誤的線程上執(zhí)行的邏輯。
    • 如果一個對象在一個意外的時間被自動釋放,它將被釋放在任何線程的自動釋放池塊,它恰好在其中。對于只應該從一個線程獲取的資源,這可能很容易致命。

5. Collections Own the Objects They Contain - 集合擁有它們包含的對象

當您將對象添加到集合(如數(shù)組,字典或集合)時,集合將擁有該集合的所有權。 當對象從集合中移除或集合本身被釋放時,集合將放棄所有權。 因此,例如,如果要創(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];
}

在這種情況下,您沒有調用alloc,所以不需要調用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)的范圍內發(fā)送allocedNumber釋放消息以平衡alloc。 由于數(shù)組在addObject添加時retain number,所以在數(shù)組中不會釋放它。

要理解這一點,把自己置于執(zhí)行收集類的person的位置。 你想確保沒有給你的對象從你的下面消失,所以你傳送給他們retain消息,如果它們被刪除,你必須發(fā)送一個平衡release消息, 并且任何存在的對象應在您自己的dealloc方法期間發(fā)送release消息。

6. Ownership Policy Is Implemented Using Retain Counts - 使用Retain Counts實現(xiàn)所有權策略

所有權政策通過引用計數(shù)來實現(xiàn),通常在retain方法之后稱為retain count。 每個對象都有一個保留計數(shù)。

  • 創(chuàng)建對象時,它的保留計數(shù)為1。
  • 當您向對象發(fā)送retain消息時,其保留計數(shù)將遞增1。
  • 當您向對象發(fā)送release消息時,其保留計數(shù)將遞減1。
  • 當您向對象發(fā)送autorelease消息時,其引用計數(shù)在當前自動釋放池塊的末尾遞減1。
  • 如果對象的引用計數(shù)減少為零,則將其釋放。

重要:應該沒有不需要明確的說明一個對象它的保留計數(shù)是什么(參見retainCount)。 結果通常是誤導性的,因為您可能不知道框架對象是否保留了您感興趣的對象。 在調試內存管理問題時,您應該注意確保代碼遵守所有權規(guī)則。


Using Autorelease Pool Blocks - 使用自動釋放池塊

自動釋放池塊提供了一種機制,您可以放棄對象的所有權,但避免立即釋放它的可能性(例如從方法返回對象時)。 通常,您不需要創(chuàng)建自己的自動釋放池塊,但是有一些情況,您必須或有必要這樣做。

1. About Autorelease Pool Blocks - 關于自動釋放池塊

使用@autoreleasepool標記自動釋放池塊,如以下示例所示:

@autoreleasepool {
    // Code that creates autoreleased objects.
}

在自動釋放池塊的末尾,在塊內接收到自動釋放消息的對象被發(fā)送一個release消息 - 對象每次在塊內發(fā)送一個autorelease消息時都會收到一個release消息。

像任何其他代碼塊一樣,自動釋放池塊可以嵌套:

@autoreleasepool {
    // . . .
    @autoreleasepool {
        // . . .
    }
    . . .
}

(通常您通常不會像上面那樣看到代碼,通常,一個源文件中的自動釋放池塊中的代碼將調用另一個自動釋放池塊中包含的另一個源文件中的代碼。)對于給定的autorelease消息,相應的release消息在自動釋放池塊的末尾發(fā)送autorelease消息時被發(fā)送。

Cocoa總是期望在自動釋放池塊中執(zhí)行代碼,否則自動釋放的對象不會被釋放,并且應用程序泄漏內存。 (如果您在自動釋放池塊之外發(fā)送autorelease消息,Cocoa會記錄一個合適的錯誤消息。)AppKitUIKit框架處理自動釋放池塊中的每個事件循環(huán)迭代(例如鼠標向下事件或點擊)。因此,您通常不必自己創(chuàng)建自動釋放池塊,甚至可以看到用于創(chuàng)建自動釋放池塊的代碼。但是,有三種情況可能會使用您自己的autorelease池塊:

  • 如果您正在編寫一個不基于UI框架的程序,例如命令行工具。
  • 如果你寫一個創(chuàng)建許多臨時對象的循環(huán)。
    • 您可以在循環(huán)中使用自動釋放池塊在下一次迭代之前處理這些對象。在循環(huán)中使用自動釋放池塊有助于減少應用程序的最大內存占用。
  • 如果你產生一個二級線程。
    • 一旦線程開始執(zhí)行,您必須創(chuàng)建自己的自動釋放池塊;否則,您的應用程序將泄漏對象。 (有關詳細信息,請參閱Autorelease Pool Blocks and Threads 。)

2. Use Local Autorelease Pool Blocks to Reduce Peak Memory Footprint - 使用本地自動釋放池塊來減少峰值內存占用

許多程序創(chuàng)建自動釋放的臨時對象。 這些對象增加了程序的內存占用空間,直到塊的結尾。 在許多情況下,允許臨時對象累積直到當前事件循環(huán)迭代結束不會導致過多的開銷;但是在某些情況下,您可能會創(chuàng)建大量臨時對象,從而大大增加內存占用空間,并希望更快地處理它們。 在后一種情況下,您可以創(chuàng)建自己的自動釋放池塊。 在塊的末尾,臨時對象被釋放,這通常導致其釋放,從而減少程序的內存占用。

以下示例顯示如何在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)在塊的末尾釋放。

在自動釋放池塊之后,您應該將塊內自動釋放的任何對象視為disposed of。不要向該對象發(fā)送消息或將其返回給方法的調用者。 如果您必須使用超出自動釋放池塊的臨時對象,則可以通過向塊中的對象發(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:的調用者。

3. Autorelease Pool Blocks and Threads - 自動釋放池塊和線程

Cocoa應用程序中的每個線程都維護自己的堆棧自動釋放池塊。如果您正在編寫僅基礎程序或分離線程,則需要創(chuàng)建自己的自動釋放池塊。

如果您的應用程序或線程長期存在,并且可能會生成大量自動釋放的對象,則應使用自動釋放池塊(如主線程上的AppKit和UIKit);否則,自動釋放的對象會累積,并且您的內存占用將增長。如果您的分離線程不會使Cocoa調用,則不需要使用自動釋放池塊。

注意:如果使用POSIX線程API而不是NSThread創(chuàng)建輔助線程,則除非Cocoa處于多線程模式,否則不能使用Cocoa。 Cocoa僅在分離其第一個NSThread對象后才進入多線程模式。要在輔助POSIX線程上使用Cocoa,您的應用程序必須首先分離至少一個可以立即退出的NSThread對象。您可以使用NSThread類方法isMultiThreaded來測試Cocoa是否處于多線程模式。

后記

未完,待續(xù)~~~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1.簡介 應用程序的內存管理就是在程序的運行期開辟內存,使用內存,使用完畢后釋放內存。一個好的程序使用盡可能少的內...
    BoomLee閱讀 2,030評論 8 7
  • 內存管理是程序在運行時分配內存、使用內存,并在程序完成時釋放內存的過程。在Objective-C中,也被看作是在眾...
    蹲瓜閱讀 3,360評論 1 8
  • 1. 內總管理原則(引用計數(shù)) IOS的對象都繼承于NSObject, 該對象有一個方法:retainCount...
    lilinjianshu閱讀 2,242評論 0 2
  • 29.理解引用計數(shù) Objective-C語言使用引用計數(shù)來管理內存,也就是說,每個對象都有個可以遞增或遞減的計數(shù)...
    Code_Ninja閱讀 1,727評論 1 3
  • 我有個朋友,她的朋友是一個矮萌妹子,叫相相。今天我們就來說說這個相相的故事。 看人嘛,首先是長相,我認識她的時候,...
    北辰北閱讀 482評論 0 1

友情鏈接更多精彩內容