你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> [iOS]Advanced Memory Management Programming Guide 高級內存管理編程指南(官方文檔翻譯)

[iOS]Advanced Memory Management Programming Guide 高級內存管理編程指南(官方文檔翻譯)

編輯:IOS開發綜合

About Memory Management - 關於內存管理

Application memory management is the process of allocating memory during your program’s runtime, using it, and freeing it when you are done with it. A well-written program uses as little memory as possible. In Objective-C, it can also be seen as a way of distributing ownership of limited memory resources among many pieces of data and code. When you have finished working through this guide, you will have the knowledge you need to manage your application’s memory by explicitly managing the life cycle of objects and freeing them when they are no longer needed.

應用程序的內存管理是程序運行時內存分配的過程,使用它,並當你用完它的時候釋放它。寫得好的程序應該盡可能少的使用內存。在Objective-C,它也可以被看作是分布 數據和代碼的許多塊當中的有限的內存資源的所有權的方法。當您完成通過這一指南的工作,你將有一些關於你的應用程序的內存管理知識,你需要明確管理對象的生命周期,並且當他們不再需要管理時釋放他們。

Although memory management is typically considered at the level of an individual object, your goal is actually to manage object graphs. You want to make sure that you have no more objects in memory than you actually need.

雖然內存管理通常被認為是在單獨對象的水平,你的目標其實是管理 object graphs,你要確保你在內存中沒有比實際需要 更多的對象。

 


\

 <喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMiBpZD0="at-a-glance-簡單了解">At a Glance - 簡單了解

Objective-C provides two methods of application memory management.

In the method described in this guide, referred to as “manual retain-release” or MRR, you explicitly manage memory by keeping track of objects you own. This is implemented using a model, known as reference counting, that the Foundation class NSObject provides in conjunction with the runtime environment. In Automatic Reference Counting, or ARC, the system uses the same reference counting system as MRR, but it inserts the appropriate memory management method calls for you at compile-time. You are strongly encouraged to use ARC for new projects. If you use ARC, there is typically no need to understand the underlying implementation described in this document, although it may in some situations be helpful. For more about ARC, see Transitioning to ARC Release Notes.

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

在本指南中說明的方法,通過跟蹤你自己的對象,明確地管理內存,被稱為“手動保留釋放(manual retain-release)”或MRR。這是用一個模型來實現,稱為引用計數,是基礎類 NSObject 提供的,並與運行環境相結合。 在自動引用計數,或ARC,該系統使用相同的引用計數系統MRR,但它在編譯的時候要求你插入適當的內存管理方法。強烈建議您為新項目使用ARC。如果使用ARC,通常沒有必要理解本文檔中描述的底層實現,雖然它可能在某些情況下是有益的。欲了解更多有關ARC,請參閱 Transitioning to ARC Release Notes。

There are two main kinds of problem that result from incorrect memory management:

Freeing or overwriting data that is still in use

This causes memory corruption, and typically results in your application crashing, or worse, corrupted user data.

Not freeing data that is no longer in use causes memory leaks

A memory leak is where allocated memory is not freed, even though it is never used again. Leaks cause your application to use ever-increasing amounts of memory, which in turn may result in poor system performance or your application being terminated.

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

釋放或改寫仍在使用的數據

這會導致內存損壞,通常會導致應用程序崩潰,或者更糟,破壞用戶數據。

不釋放不再使用的數據導致內存洩漏

內存洩漏就是分配的內存不釋放,即使它永遠不會再次使用。洩漏導致您的應用程序使用的內存量的不斷增加,這反過來又可能導致較差的系統性能或您的應用程序被終止。

Thinking about memory management from the perspective of reference counting, however, is frequently counterproductive, because you tend to consider memory management in terms of the implementation details rather than in terms of your actual goals. Instead, you should think of memory management from the perspective of object ownership and object graphs.

然而,從引用計數的角度思考內存管理,經常是適得其反,因為你往往會考慮內存管理的實施細則方面,而不是在你的實際目標方面。相反,你應該從對象所有權和 object graphs 的角度去思考內存管理。

Cocoa uses a straightforward naming convention to indicate when you own an object returned by a method.

當你擁有被一個方法返回的對象,Cocoa 使用一個簡單的命名約定來指明。

See Memory Management Policy.

參閱 Memory Management Policy

Although the basic policy is straightforward, there are some practical steps you can take to make managing memory easier, and to help to ensure your program remains reliable and robust while at the same time minimizing its resource requirements.

雖然基本ce’lue是直接的,也有一些實際的步驟,你可以使內存管理更容易,並幫助確保您的程序是可靠的和有魯棒性的同時減少資源需求。

See Practical Memory Management.

參閱 Practical Memory Management

Autorelease pool blocks provide a mechanism whereby you can send an object a “deferred” release message. This is useful in situations where you want to relinquish ownership of an object, but want to avoid the possibility of it being deallocated immediately (such as when you return an object from a method). There are occasions when you might use your own autorelease pool blocks.

自動釋放池模塊提供了一個機制,使您可以發送對象的“延遲” release 消息。你想放棄一個對象的所有權,但要避免它立刻釋放(如你從一個方法返回一個對象時),這是很有用的。有些時候你可能會使用自己的自動釋放池

See Using Autorelease Pool Blocks.

參閱 Using Autorelease Pool Blocks

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

To identify problems with your code at compile time, you can use the Clang Static Analyzer that is built into Xcode.

If memory management problems do nevertheless arise, there are other tools and techniques you can use to identify and diagnose the issues.

Many of the tools and techniques are described in Technical Note TN2239, iOS Debugging Magic, in particular the use of NSZombie to help find over-released object. You can use Instruments to track reference counting events and look for memory leaks. See Collecting Data on Your App.

為了確定你的代碼在編譯時出現問題,則可以使用 Clang Static Analyzer ,內置在Xcode中。

如果你仍然出現內存管理問題,還有其他的工具和技術可以用來識別和診斷問題。

許多工具和技術的技術說明在 Technical Note TN2239 中有描述,iOS Debugging Magic ,特別是使用的 NSZombie ,以幫助找到還未釋放的對象。 您可以使用儀器來跟蹤引用計數的事件,並查找內存洩漏。請參閱 Collecting Data on Your App

Memory Management Policy - 內存管理策略

The basic model used for memory management in a reference-counted environment is provided by a combination of methods defined in the NSObject protocol and a standard method naming convention. The NSObject class also defines a method, dealloc, that is invoked automatically when an object is deallocated. This article describes all the basic rules you need to know to manage memory correctly in a Cocoa program, and provides some examples of correct usage.

在一個引用計數環境用於內存管理的基本模型 是由 在 NSObject 協議和標准方法命名約定中所定義的方法的組合提供的。該NSObject類也定義了一種方法,dealloc,當一個對象被釋放時自動調用。本文介紹了所有你需要知道的 Cocoa 程序正確處理內存中的基本規則,並提供了一些正確的用法的例子。

Basic Memory Management Rules - 基本內存管理規則

The memory management model is based on object ownership. Any object may have one or more owners. As long as an object has at least one owner, it continues to exist. If an object has no owners, the runtime system destroys it automatically. To make sure it is clear when you own an object and when you do not, Cocoa sets the following policy:

You own any object you create

You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy” (for example, alloc, newObject, or mutableCopy).

You can take ownership of an object using retain

A received object is normally guaranteed to remain valid within the method it was received in, and that method may also safely return the object to its invoker. You use retain in two situations: (1) In the implementation of an accessor method or an init method, to take ownership of an object you want to store as a property value; and (2) To prevent an object from being invalidated as a side-effect of some other operation (as explained in Avoid Causing Deallocation of Objects You’re Using).

When you no longer need it, you must relinquish ownership of an object you own

You relinquish ownership of an object by sending it a release message or an autorelease message. In Cocoa terminology, relinquishing ownership of an object is therefore typically referred to as “releasing” an object.

You must not relinquish ownership of an object you do not own

This is just corollary of the previous policy rules, stated explicitly.

內存管理模型是基於對象的所有權。任何對象可具有一個或多個所有者。只要對象具有至少一個所有者,它繼續存在。 如果一個對象沒有所有者,運行時系統會自動將它摧毀。為了確保清晰的知道當你擁有一個對象或者沒有擁有,Cocoa提出以下策略:

你擁有你所創建的任何對象

使用 “alloc”, “new”, “copy”, or “mutableCopy”(例如,alloc , newObject 或 mutableCopy )這種類型開頭的方法來創建一個對象。

可以使用 retain 保留對象的所有權

接收到的對象通常是保證其接收的方法中仍然有效,而且方法也可以安全地將對象返回到它的調用。在兩種情況下您可以使用 retain ,(1)在存取方法或 init方法的實現中,你希望將得到的返回對象作為成員變量(property)來存儲。(2)在執行某些操作時,為了防止對象被廢止。(例如 Avoid Causing Deallocation of Objects You’re Using)

當你不再需要它,你必須放棄你自己的對象的所有權

您可以通過發送一個 release 消息或 autorelease 消息放棄對象的所有權。在Cocoa 術語中,所謂 放棄所有權,就是 release 一個對象。

對於你不擁有的對象,不要嘗試放棄所有權

這是上面的策略規則的推論,明確表示。

A Simple Example - 一個簡單的例子

為了說明這個策略,可以考慮下面的代碼片段:

{
    Person *aPerson = [[Person alloc] init];
    // ...
    NSString *name = aPerson.fullName;
    // ...
    [aPerson release];
}

The Person object is created using the alloc method, so it is subsequently sent a release message when it is no longer needed. The person’s name is not retrieved using any of the owning methods, so it is not sent a release message. Notice, though, that the example uses release rather than autorelease.

Person對象是使用alloc方法創建,所以不再需要它時候,接著發送一個 release的消息給它。人的姓名未使用任何擁有方法的檢索,因此它不發送一個release消息。請注意,該示例使用release ,而不是autorelease 。

Use autorelease to Send a Deferred release - 使用autorelease發送延遲release

You use autorelease when you need to send a deferred release message—typically when returning an object from a method. For example, you could implement the fullName method like this:

當你需要發送延遲release消息,可以使用autorelease ,通常當從方法返回一個對象時。例如,你可以實現的fullName這樣的方法:

- (NSString *)fullName {
    NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@",
                                          self.firstName, self.lastName] autorelease];
    return string;
}

You own the string returned by alloc. To abide by the memory management rules, you must relinquish ownership of the string before you lose the reference to it. If you use release, however, the string will be deallocated before it is returned (and the method would return an invalid object). Using autorelease, you signify that you want to relinquish ownership, but you allow the caller of the method to use the returned string before it is deallocated.

You could also implement the fullName method like this:

你自己通過 alloc 返回一個 string。遵守內存管理規則,你必須在失去引用之前放棄該字符串的所有權。但是如果使用release,字符串將在返回之前被釋放(並且該方法將返回一個無效對象)。使用autorelease,你表示要放棄所有權,但允許方法的調用者在它被銷毀之前使用返回的字符串。

你也可以像下面這樣實現fullName的方法:

- (NSString *)fullName {
    NSString *string = [NSString stringWithFormat:@"%@ %@",
                                 self.firstName, self.lastName];
    return string;
}

Following the basic rules, you don’t own the string returned by stringWithFormat:, so you can safely return the string from the method.

By way of contrast, the following implementation is wrong:

遵循基本規則,通過 stringWithFormat返回的字符串,你並不擁有它,(譯者:請注 意到這裡並沒有使用 alloc,方法名也不是以 init 開始),這樣你就可以放心地從該方法返回字符串。

通過對比的方式,下面的實施是錯誤的 :

- (NSString *)fullName {
    NSString *string = [[NSString alloc] initWithFormat:@"%@ %@",
                                         self.firstName, self.lastName];
    return string;
}

According to the naming convention, there is nothing to denote that the caller of the fullName method owns the returned string. The caller therefore has no reason to release the returned string, and it will thus be leaked.

根據命名規則,沒有地方說明 fullName 方法的調用者擁有返回的 string。因此,調用者沒有理由釋放返回的字符串,它會因此被洩露。

You Don’t Own Objects Returned by Reference - 你沒有通過引用返回的對象的所有權

Some methods in Cocoa specify that an object is returned by reference (that is, they take an argument of type ClassName * or id ). A common pattern is to use an NSError object that contains information about an error if one occurs, as illustrated by initWithContentsOfURL:options:error: (NSData) and initWithContentsOfFile:encoding:error: (NSString).

In these cases, the same rules apply as have already been described. When you invoke any of these methods, you do not create the NSError object, so you do not own it. There is therefore no need to release it, as illustrated in this example:

Cocoa 中的一些方法指定一個對象是通過引用返回(即 它們的返回類型是 ClassName ** 或者 id *),常見的情況是當出現錯誤異常時,一個 NSError 對象被用來承載錯誤的信息。比如 initWithContentsOfURL:options:error: (NSData) 和 initWithContentsOfFile:encoding:error: (NSString)。

在這些情況下,同樣的規則適用。當你調用任何這些方法,你沒有創建 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];

Implement dealloc to Relinquish Ownership of Objects - 實現dealloc放棄對象的所有權

The NSObject class defines a method, dealloc, that is invoked automatically when an object has no owners and its memory is reclaimed—in Cocoa terminology it is “freed” or “deallocated.”. The role of the dealloc method is to free the object’s own memory, and to dispose of any resources it holds, including ownership of any object instance variables.

The following example illustrates how you might implement a dealloc method for a Person class:

NSObject類定義的方法,dealloc,這個方法在對象無主(沒有所有者)的情況下, 當內存回收的時候會由系統自動調用,Cocoa術語說,就是 freed 或者 deallocated。dealloc方法的作用就是釋放對象自身的內存,以及處置已持有的任何資源,包括任何對象的實例變量的所有權。

下面的例子說明了如何實現一個Person類的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

Important: Never invoke another object’s dealloc method directly.
You must invoke the superclass’s implementation at the end of your implementation.
You should not tie management of system resources to object lifetimes; see Don’t Use dealloc to Manage Scarce Resources.
When an application terminates, objects may not be sent a dealloc message. Because the process’s memory is automatically cleared on exit, it is more efficient simply to allow the operating system to clean up resources than to invoke all the memory management methods.

重要提示:不要直接調用另一個對象的dealloc方法。
你必須在你自己的實現的結束調用父類的實現。
你不可以把系統的資源和對象的生命周期進行綁定。參閱 Don’t Use dealloc to Manage Scarce Resources
當應用程序終止時,對象可能無法發送dealloc消息。因為該方法的內存被自動退出清零,讓操作系統清理資源比調用所有的內存管理方法更有效。

Core Foundation Uses Similar but Different Rules - Core Foundation使用了類似但不同的規則

There are similar memory management rules for Core Foundation objects (see Memory Management Programming Guide for Core Foundation). The naming conventions for Cocoa and Core Foundation, however, are different. In particular, Core Foundation’s Create Rule (see The Create Rule) does not apply to methods that return Objective-C objects. For example, in the following code fragment, you are not responsible for relinquishing ownership of myInstance:

對於 Core Foundation 對象,有一些相似的內存管理規則(參閱 Memory Management Programming Guide for Core Foundation)。但是 對於 Cocoa 和 Core Foundation 命名規則是不同的。具體說,就是 Core Foundatoin 的創建規則,並不適用於返回Objective-C的對象的方法。 例如,在下面的代碼片段,你沒有責任或義務來釋放對 myInstance 的所有權:

MyClass *myInstance = [MyClass createInstance];

內存管理實踐

Although the fundamental concepts described in Memory Management Policy are straightforward, there are some practical steps you can take to make managing memory easier, and to help to ensure your program remains reliable and robust while at the same time minimizing its resource requirements.

雖然在Memory Management Policy 中描述的基本概念很簡單,有一些實際的步驟,你可以使內存管理更容易,並幫助確保您的程序是可靠的和魯棒性的同時減少資源需求。

Use Accessor Methods to Make Memory Management Easier - 使用訪問器方法使內存管理更容易

If your class has a property that is an object, you must make sure that any object that is set as the value is not deallocated while you’re using it. You must therefore claim ownership of the object when it is set. You must also make sure you then relinquish ownership of any currently-held value.

Sometimes it might seem tedious or pedantic, but if you use accessor methods consistently, the chances of having problems with memory management decrease considerably. If you are using retain and release on instance variables throughout your code, you are almost certainly doing the wrong thing.

Consider a Counter object whose count you want to set.

如果你的類有一個屬性是一個對象,你必須確保,當你使用它,被設置為值的任何對象都不釋放。 因此,當它被設置時,必須聲明對象的所有權。你還必須保證對這些對象 所有權的放棄。

有時候似乎很麻煩,如果你堅持用 get 和 set 這種方法方法來實現,那麼內存管理的問題出現幾率就大幅度減少了。如果對於整個代碼的實例變量,使用的是retain和release,你幾乎可以肯定是在做錯誤的事情。

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

The property declares two accessor methods. Typically, you should ask the compiler to synthesize the methods; however, it’s instructive to see how they might be implemented.

In the “get” accessor, you just return the synthesized instance variable, so there is no need for retain or release:

屬性聲明了兩個訪問器方法。通常情況下,你應該要求編譯器來 synthesize 這些方法;但是,看一下它們可能被實現的形式是有幫助的。

get 訪問器,就是返回 synthesized實例變量,所以沒有必要retain或release :

- (NSNumber *)count {
    return _count;
}

In the “set” method, if everyone else is playing by the same rules you have to assume the new count may be disposed of at any time so you have to take ownership of the object—by sending it a retain message—to ensure it won’t be. You must also relinquish ownership of the old count object here by sending it a release message. (Sending a message to nil is allowed in Objective-C, so the implementation will still work if _count hasn’t yet been set.) You must send this after [newCount retain] in case the two are the same object—you don’t want to inadvertently cause it to be deallocated.

set方法中,如果每個人都遵守相同的規則,你必須承擔起新的計數可在任何時間進行設置,所以你必須通過發送一個retain的消息確保它不會被銷毀,來維持住對象的所有權。此外,還必須通過發送一個 release 消息放棄老的計數對象在這裡的所有權。(在Objective-C發送消息 nil 是允許的,所以如果實現,因此就算_count 還沒有舊值,也不會出錯。)你必須 在[newCount retain]之後再(對舊值)發送 release,因為你不想因為意外而造成 dealloc。

- (void)setCount:(NSNumber *)newCount {
    [newCount retain];
    [_count release];
    // Make the new assignment.
    _count = newCount;
}

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

Suppose you want to implement a method to reset the counter. You have a couple of choices. The first implementation creates the NSNumber instance with alloc, so you balance that with a release.

假設你想實現復位計數器的方法。你有幾個選擇。第一種做法就是用 alloc 來新建一個 NSNumber 實例,然後再對應一個 release 。

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

The second uses a convenience constructor to create a new NSNumber object. There is therefore no need for retain or release messages

第二個使用快速構造器來創建一個新 NSNumber 對象。 因此,不需要 retain 或 release 消息

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

Note that both use the set accessor method.

The following will almost certainly work correctly for simple cases, but as tempting as it may be to eschew accessor methods, doing so will almost certainly lead to a mistake at some stage (for example, when you forget to retain or release, or if the memory management semantics for the instance variable change).

需要注意的是兩者都使用set訪問方法。

下面的做法,對於簡單的情況而言,肯定是沒問題的。但是,因為它的實現繞開了 set 方法, 那麼在特定情況下會導致錯誤(例如,比如當你忘記了 retain 或者 release,或者如果實例變量的內存管理發生了變化)。

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

Note also that if you are using key-value observing, then changing the variable in this way is not KVO compliant.

還需要注意的是,如果你使用 key-value observing,那麼這種對於值的復位就跟 KVO 不 兼容了。

Don’t Use Accessor Methods in Initializer Methods and dealloc - 不要在初始化方法和 dealloc中使用訪問器方法

The only places you shouldn’t use accessor methods to set an instance variable are in initializer methods and dealloc. To initialize a counter object with a number object representing zero, you might implement an init method as follows:

你不應該使用存取方法來設置實例變量的唯一地方是在初始化方法和dealloc 。為了初始化一個 counter,並將值設置為 0,你可以實現一個初始化方法如下:

- init {
    self = [super init];
    if (self) {
        _count = [[NSNumber alloc] initWithInteger:0];
    }
    return self;
}

To allow a counter to be initialized with a count other than zero, you might implement an initWithCount: method as follows:

為了讓 counter 的初始化值為非 0 值,你可以實現一個名為 initWithCount:的方法:

- initWithCount:(NSNumber *)startingCount {
    self = [super init];
    if (self) {
        _count = [startingCount copy];
    }
    return self;
}

Since the Counter class has an object instance variable, you must also implement a dealloc method. It should relinquish ownership of any instance variables by sending them a release message, and ultimately it should invoke super’s implementation:

由於計數器類有一個對象的實例變量,還必須實現一個dealloc方法。它應該通過發送一個release消息放棄任何實例變量的所有權,最終也應該調用父類的實現:

- (void)dealloc {
    [_count release];
    [super dealloc];
}

Use Weak References to Avoid Retain Cycles - 使用弱引用來避免所有權的死鎖

Retaining an object creates a strong reference to that object. An object cannot be deallocated until all of its strong references are released. A problem, known as a retain cycle, can therefore arise if two objects may have cyclical references—that is, they have a strong reference to each other (either directly, or through a chain of other objects each with a strong reference to the next leading back to the first).

Retain 一個對象,實際是對一個對象的強引用(strong reference)。一個對象在所有的強引用都解除之前,是不能被 dealloc 的,這導致一個被稱為“環形持有”的問題:兩個對象相互強引用 (可能是直接引用,也可能是通過其他對象間接地引用。)

The object relationships shown in Figure 1 illustrate a potential retain cycle. The Document object has a Page object for each page in the document. Each Page object has a property that keeps track of which document it is in. If the Document object has a strong reference to the Page object and the Page object has a strong reference to the Document object, neither object can ever be deallocated. The Document’s reference count cannot become zero until the Page object is released, and the Page object won’t be released until the Document object is deallocated.

下圖所示的對象關系就構成了一個環形持有。Document 對象持有多個 Page 對象,每個 Page 對象又具有一個 Document 引用來指示它歸屬的文檔。全部 Page 對象都 release 之前,Document 對象的引用數永遠不會為 0;而如果 Document 對象存在,Page 對象也無法被 release。

 


Figure 1  An illustration of cyclical references

 

The solution to the problem of retain cycles is to use weak references. A weak reference is a non-owning relationship where the source object does not retain the object to which it has a reference.

環形持有問題的解決方案是使用弱引用。弱引用是一個非持有關系,已經被引用的對象不對它的擁有者進行持有。

To keep the object graph intact, however, there must be strong references somewhere (if there were only weak references, then the pages and paragraphs might not have any owners and so would be deallocated). Cocoa establishes a convention, therefore, that a “parent” object should maintain strong references to its “children,” and that the children should have weak references to their parents.

為了實現上面的對象圖,肯定是需要強引用的(如果只有弱引用,那麼 Page 和 Paragraph 就沒有了持有者,造成他們不會被 dealloc),因此 Cocoa 建立了一個約定,父對象應該維持對於其子對象的強引用,並且子對象應該只對父對象建立弱引用。

So, in Figure 1 the document object has a strong reference to (retains) its page objects, but the page object has a weak reference to (does not retain) the document object.

所以,圖1種 document 對象對 page 有一個強引用(retains),但是page對象對 document 對象有一個弱引用(不是 retain)。

Examples of weak references in Cocoa include, but are not restricted to, table data sources, outline view items, notification observers, and miscellaneous targets and delegates.

Cocoa 中包含了弱引用的例子,但不限於,表中的數據源,大綱視圖項, notification 觀察員,以及其他的 target 以及 delegate。

You need to be careful about sending messages to objects for which you hold only a weak reference. If you send a message to an object after it has been deallocated, your application will crash. You must have well-defined conditions for when the object is valid. In most cases, the weak-referenced object is aware of the other object’s weak reference to it, as is the case for circular references, and is responsible for notifying the other object when it deallocates. For example, when you register an object with a notification center, the notification center stores a weak reference to the object and sends messages to it when the appropriate notifications are posted. When the object is deallocated, you need to unregister it with the notification center to prevent the notification center from sending any further messages to the object, which no longer exists. Likewise, when a delegate object is deallocated, you need to remove the delegate link by sending a setDelegate: message with a nil argument to the other object. These messages are normally sent from the object’s dealloc method.

你必須小心將消息發送到您持有只是一個弱引用的對象。當你發送消息給一個被 dealloc 的弱引用對象時,你的應用程序會崩潰。因此,你必須細致地判斷對象是否有效。多數情況下,被弱引用的對象是知道其 他對象對它的弱引用的(比如環形持有的情形),所以需要通知其他對象它自己的 dealloc。舉例, 當你向Notification Center 注冊一個對象時,Notification Center對這個對象是弱引用的,並且在有消息需要通知到這個對象時,就發送消息給這個對象。當這個對象 dealloc 的時候,你必須向 Notification Center 取消這個對象的注冊。這樣,這個 Notification Center 就不會再發送消息給這個 不存在的對象了。同樣,當一個 delegate 對象被 dealloc 的時候,必須向其他對象發送一個 setDelegate:消息,並傳遞 nil 參數,從而將代理的關系撤銷。這些消息通常在對象的 dealloc 方法中發出。

Avoid Causing Deallocation of Objects You’re Using - 避免你正在使用的對象被 dealloc

Cocoa’s ownership policy specifies that received objects should typically remain valid throughout the scope of the calling method. It should also be possible to return a received object from the current scope without fear of it being released. It should not matter to your application that the getter method of an object returns a cached instance variable or a computed value. What matters is that the object remains valid for the time you need it.

Cocoa 的所有權策略規定,收到的對象通常應該在整個調用方法的范圍仍然有效。這也應該是在當前方法內部,不必擔心你收到的返回對象會被 dealloc 。對象的 getter 方法返回一個被緩存的實例或者一個計算出來的值,這並不重要,重要的是這個對象在你使用它的時候會一直有效。

There are occasional exceptions to this rule, primarily falling into one of two categories.

偶爾有例外的情況,主要分為兩類:

When an object is removed from one of the fundamental collection classes.

當一個對象從 collection classes 中刪除的時候。
heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// heisenObject could now be invalid.

When an object is removed from one of the fundamental collection classes, it is sent a release (rather than autorelease) message. If the collection was the only owner of the removed object, the removed object (heisenObject in the example ) is then immediately deallocated.

當一個對象從基本集合類之一刪除,它發送一個 release (而不是 autorelease )消息。如果集合是被刪除對象的唯一擁有者,被移除的對象是立即被釋放。

When a “parent object” is deallocated.

當一個“父對象”被釋放。
id parent = <#create a parent object#>;
// ...
heisenObject = [parent child] ;
[parent release]; // Or, for example: self.parent = nil;
// heisenObject could now be invalid.

In some situations you retrieve an object from another object, and then directly or indirectly release the parent object. If releasing the parent causes it to be deallocated, and the parent was the only owner of the child, then the child (heisenObject in the example) will be deallocated at the same time (assuming that it is sent a release rather than an autorelease message in the parent’s dealloc method).

在某些情況下檢索來自另一個對象的對象,然後直接或間接地釋放父對象。如果釋放父對象導致它被釋放,並且父對象是子對象的唯一所有者,那麼子對象(例子中的heisenObject)將在同一時間被釋放(假設在父類中的 dealloc 方法中,給子對象發送的是 release 消息,而不是 autolease 消息)

To protect against these situations, you retain heisenObject upon receiving it and you release it when you have finished with it. For example:

為了防止這種情況下,你可以在接收到heisenObject 的時候 retain 一次,並且當你用完的時候 ,release。例如:

heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// Use heisenObject...
[heisenObject release];

Don’t Use dealloc to Manage Scarce Resources - 不要使用 dealloc 來管理關鍵系統資源

You should typically not manage scarce resources such as file descriptors, network connections, and buffers or caches in a dealloc method. In particular, you should not design classes so that dealloc will be invoked when you think it will be invoked. Invocation of dealloc might be delayed or sidestepped, either because of a bug or because of application tear-down.

通常,你不應該在 dealloc 中來管理稀缺系統資源,比如文件描述符、網絡連接、緩存等。尤其注意,你不應該這樣設計類:你想讓系統什麼時候調用 dealloc,系統就什麼時候調用。dealloc 的調用可能會被推遲或者擱置,比如因為 bug 或者系統性能下降。

Instead, if you have a class whose instances manage scarce resources, you should design your application such that you know when you no longer need the resources and can then tell the instance to “clean up” at that point. You would typically then release the instance, and dealloc would follow, but you will not suffer additional problems if it does not.

相反,如果你有一個類,管理了稀缺資源,它就必須知道它什麼時候不再需要這些資源,並在此時立即釋放資源。通常情況下,此時,你會調用 release 來 dealloc,但是因 為此前你已經釋放了資源,這裡就不會遇到任何問題。

Problems may arise if you try to piggy-back resource management on top of dealloc. For example:

如果你嘗試把資源管理問題的職能交給 dealloc ,可能會導致很多問題,比如:

Order dependencies on object graph tear-down.

The object graph tear-down mechanism is inherently non-ordered. Although you might typically expect—and get—a particular order, you are introducing fragility. If an object is unexpectedly autoreleased rather than released for example, the tear-down order may change, which may lead to unexpected results.

Non-reclamation of scarce resources.

Memory leaks are bugs that should be fixed, but they are generally not immediately fatal. If scarce resources are not released when you expect them to be released, however, you may run into more serious problems. If your application runs out of file descriptors, for example, the user may not be able to save data.

Cleanup logic being executed on the wrong thread.

If an object is autoreleased at an unexpected time, it will be deallocated on whatever thread’s autorelease pool block it happens to be in. This can easily be fatal for resources that should only be touched from one thread.

對象圖的拆除順序問題

對象圖拆卸機制本質上是無序的。盡管你可能通常希望和獲得一個特定的順序。例如,如果一個對象被意外地 autorelease ,而不是 release ,拆卸順序可能改變,這可能會導致意想不到的結果。

系統稀缺資源不能回收

內存洩漏是應該被修復的 bugs,但他們一般都不會是立即致命的。然而,如果當你希望稀缺資源被釋放,但沒有釋放,你可能會遇到更嚴重的問題。例如,如果你的應用程序運行了文件描述符,用戶可能無法保存數據。

釋放資源的操作被錯誤的線程執行

如果對象是在一個意想不到的時間自動釋放,它將被線程池中的線程來 dealloc。對於只能從一個線程操作的資源來說,這很容易造成致命的後果。

Collections Own the Objects They Contain - Collections 擁有他們所包含的對象

When you add an object to a collection (such as an array, dictionary, or set), the collection takes ownership of it. The collection will relinquish ownership when the object is removed from the collection or when the collection is itself released. Thus, for example, if you want to create an array of numbers you might do either of the following:

當您添加一個對象到一個 collection ,例如(數組,字典,集合),collection會取得該對象的所有權。當對象從集合中刪除或當集合本身釋放時,集合將放棄所有權。因此,如果你想創建數字數組,可以像下面這樣做:

NSMutableArray *array = <#Get a mutable array#>;
NSUInteger i;
// ...
for (i = 0; i < 10; i++) {
    NSNumber *convenienceNumber = [NSNumber numberWithInteger:i];
    [array addObject:convenienceNumber];
}

In this case, you didn’t invoke alloc, so there’s no need to call release. There is no need to retain the new numbers (convenienceNumber), since the array will do so.

在這種情況下,你沒有調用alloc ,所以沒有必要調用release 。 沒有必要保留新numbers( convenienceNumber ),因為數組會這麼做。

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];
}

In this case, you do need to send allocedNumber a release message within the scope of the for loop to balance the alloc. Since the array retained the number when it was added by addObject:, it will not be deallocated while it’s in the array.

這種做法,我們在 for 循環內部向 allocedNumber 發送了與 alloc 相對應的 release 消息。因為Array 的 addObject: 方法實際上對這個對象做了 retain 處理,那麼這個對象(allocedNumber)不會因此而被 dealloc。

To understand this, put yourself in the position of the person who implemented the collection class. You want to make sure that no objects you’re given to look after disappear out from under you, so you send them a retain message as they’re passed in. If they’re removed, you have to send a balancing release message, and any remaining objects should be sent a release message during your own dealloc method.

要理解這一點,把自己放在那些實現集合類的人的位置。你要確保加入的對象只要繼續 存在於 Collection 裡,就不應該被 dealloc,因此你在添加這個對象時,向它發送了 retain 消息,刪 除這個對象時,向它發送了 release 消息。當你這個 collection 類自己 dealloc 時,對容器內所有的 對象發 release。

Ownership Policy Is Implemented Using Retain Counts - 通過引用計數實現所有權策略

The ownership policy is implemented through reference counting—typically called “retain count” after the retain method. Each object has a retain count.

When you create an object, it has a retain count of 1. When you send an object a retain message, its retain count is incremented by 1. When you send an object a release message, its retain count is decremented by 1. When you send an object a autorelease message, its retain count is decremented by 1 at the end of the current autorelease pool block. If an object’s retain count is reduced to zero, it is deallocated.

所有權政策是通過引用計數實現的,通常retain方法後被稱為“retain count”後。每個對象都有一個引用計數。

當你創建一個對象,它有一個保留計數 1。 當你給對象發送一個 retain 。保留計數 +1 當你給對象發送一個 release 消息,它的保留計數 -1 當你給對象發送一個 autorelease 消息。它的保留計數在當前自動釋放池塊結束後 -1 如果對象的保留計數減少到 0,它被釋放。

Important: There should be no reason to explicitly ask an object what its retain count is (see retainCount). The result is often misleading, as you may be unaware of what framework objects have retained an object in which you are interested. In debugging memory management issues, you should be concerned only with ensuring that your code adheres to the ownership rules.

重要:其實你應該沒有理由想知道一個對象的 retain count。這個數值有時候會造成對你的誤導:你不知道實際上有些系統框架的對象會對你關注的那個對象進行retain。在調試內存問題的時候,你只需要遵守所有權規則就行了。

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

Autorelease pool blocks provide a mechanism whereby you can relinquish ownership of an object, but avoid the possibility of it being deallocated immediately (such as when you return an object from a method). Typically, you don’t need to create your own autorelease pool blocks, but there are some situations in which either you must or it is beneficial to do so.

自動釋放池塊提供了一種機制,讓你可以放棄對象的所有權,但要避免它被立即釋放的可能性(例如,當您從一個方法返回一個對象)。通常情況下,你不需要創建自己的自動釋放池塊,但也有一些情況,需要自行創建。

About Autorelease Pool Blocks - 關於自動釋放池塊

An autorelease pool block is marked using @autoreleasepool, as illustrated in the following example:

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

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

At the end of the autorelease pool block, objects that received an autorelease message within the block are sent a release message—an object receives a release message for each time it was sent an autorelease message within the block.

在 autorelease 池被 dealloc 的時候,它自己會給容納的所有對象發送 release 消息。一個對象可以被多次放到同一個 autorelease 池,每一次放入(發送 autorelease 消息)都會造成將來收到一次 release。

Like any other code block, autorelease pool blocks can be nested:

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

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

(You wouldn’t normally see code exactly as above; typically code within an autorelease pool block in one source file would invoke code in another source file that is contained within another autorelease pool block.) For a given autorelease message, the corresponding release message is sent at the end of the autorelease pool block in which the autorelease message was sent.

Cocoa always expects code to be executed within an autorelease pool block, otherwise autoreleased objects do not get released and your application leaks memory. (If you send an autorelease message outside of an autorelease pool block, Cocoa logs a suitable error message.) The AppKit and UIKit frameworks process each event-loop iteration (such as a mouse down event or a tap) within an autorelease pool block. Therefore you typically do not have to create an autorelease pool block yourself, or even see the code that is used to create one. There are, however, three occasions when you might use your own autorelease pool blocks:

If you are writing a program that is not based on a UI framework, such as a command-line tool.

If you write a loop that creates many temporary objects.
You may use an autorelease pool block inside the loop to dispose of those objects before the next iteration. Using an autorelease pool block in the loop helps to reduce the maximum memory footprint of the application.

If you spawn a secondary thread.
You must create your own autorelease pool block as soon as the thread begins executing; otherwise, your application will leak objects. (See Autorelease Pool Blocks and Threads for details.)

(你不會經常看到像上面那樣的代碼;通常在一個源文件自動釋放池塊中的代碼將調用包含在另一個自動釋放池塊中的另一個源文件的代碼。)對於給定的 autorelease的 消息,相應的 release 消息在該 autorelease 池塊結束時發送 autorelease 消息。

Cocoa 總是代碼是在一個自動釋放池塊執行,否則自動釋放對象沒有得到釋放,你的應用程序的內存洩露。(如果在自動釋放池外面發送一個 autorelease 消息,Cocoa 會記錄適當的錯誤信息)AppKit 和 UIKit 框架處理每個事件循環迭代(如鼠標按下事件或觸摸)都在自動釋放池塊中。因此,你一般不必創建一個自動釋放池,甚至不需要知道創建 autorelease 池的代碼如何寫。但是,三種情況時可能會使用自己的自動釋放池塊:

如果你正在寫的不是基於一個UI框架,比如一個命令行工具的程序。 如果你寫一個循環,創建的臨時對象。

你可以在循環體內部新建一個 autorelease 池,並在一次循環結束時銷毀這些臨時對象。這樣 可以減少你的程序對內存的占用峰值。

如果您生成一個輔助線程。

必須盡快建立自己的自動釋放池塊線程開始執行; 否則,你的應用程序會洩漏的對象。 (見 Autorelease Pool Blocks and Threads)

Use Local Autorelease Pool Blocks to Reduce Peak Memory Footprint - 使用本地自動釋放池塊,降低峰值內存占用

Many programs create temporary objects that are autoreleased. These objects add to the program’s memory footprint until the end of the block. In many situations, allowing temporary objects to accumulate until the end of the current event-loop iteration does not result in excessive overhead; in some situations, however, you may create a large number of temporary objects that add substantially to memory footprint and that you want to dispose of more quickly. In these latter cases, you can create your own autorelease pool block. At the end of the block, the temporary objects are released, which typically results in their deallocation thereby reducing the program’s memory footprint.

許多程序創建的臨時對象被自動釋放。這些對象添加到程序的內存占用,直到塊的末端。在許多情況下,允許臨時對象累積,直到當前的事件循環迭代結束時沒有過多的開銷;但在有些情況下,你可能會創建大量的臨時對象,大幅增加內存占用,並且你想更加快速的銷毀。在後一種情況下,你可以創建自己的自動釋放池塊。在該塊的結束時,臨時對象被釋放,通常導致其釋放,從而減少了程序的內存占用。

The following example shows how you might use a local autorelease pool block in a for loop.

下面的例子說明了如何在 for 循環中使用本地自動釋放池塊

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. */
    }
}

The for loop processes one file at a time. Any object (such as fileContents) sent an autorelease message inside the autorelease pool block is released at the end of the block.

for循環一次處理一個文件。自動釋放池塊中的任何對象(如fileContents)發送的autorelease消息,是在塊的結尾釋放。

After an autorelease pool block, you should regard any object that was autoreleased within the block as “disposed of.” Do not send a message to that object or return it to the invoker of your method. If you must use a temporary object beyond an autorelease pool block, you can do so by sending a retain message to the object within the block and then send it autorelease after the block, as illustrated in this example:

自動釋放池塊後,你應該把對該塊作為自動釋放任何物體“進行處理。”不要發送消息到該對象或將其返回到你的方法的調用。 如果你必須在自動釋放池塊外部使用一個臨時對象,你可以在塊內發送一個 retain 消息,然後再塊之後發送 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. */
}

Sending retain to match within the autorelease pool block the and sending autorelease to it after the autorelease pool block extends the lifetime of match and allows it to receive messages outside the loop and be returned to the invoker of findMatchingObject:.

在自動釋放池塊中發送 retain 給 match,並在自動釋放池塊後發送 autorelease,拓展了match 的生命周期,並允許它在循環外接收消息並返回到 findMatchingObject: 的調用者。

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

Each thread in a Cocoa application maintains its own stack of autorelease pool blocks. If you are writing a Foundation-only program or if you detach a thread, you need to create your own autorelease pool block.

Cocoa 應用程序中的每個線程維護其自己的自動釋放池塊棧。 如果你寫的僅僅是一個基於 Foundation 的程序,或者 detach 一個線程,你需要創建自己的自動釋放池塊。

If your application or thread is long-lived and potentially generates a lot of autoreleased objects, you should use autorelease pool blocks (like AppKit and UIKit do on the main thread); otherwise, autoreleased objects accumulate and your memory footprint grows. If your detached thread does not make Cocoa calls, you do not need to use an autorelease pool block.

如果您的應用程序或線程是長期存在並可能產生大量的自動釋放的對象,你應該使用自動釋放池塊(例如AppKit和UIKit在主線程上工作);否則,自動釋放對象不斷累積,內存占用量的增長。如果你的獨立線程不讓Cocoa 調用,你不需要使用自動釋放池塊。

Note: If you create secondary threads using the POSIX thread APIs instead of NSThread, you cannot use Cocoa 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.

注意:如果您創建一個使用POSIX線程的API,而不是輔助線程 NSThread,您不能使用Cocoa,除非Cocoa在多線程模式。 為了在輔助 POSIX 線程上使用 Cocoa ,你的應用程序必須先至少 detach 一個可以立即退出的 NSThread 對象。使用 NSThread 類方法 isMultiThreaded 測試 Cocoa 是否是多線程模式。
 

Advanced Memory Management Programming Guide - 高級內存管理編程指南(官方文檔翻譯)

蘋果源文檔地址 - 點擊這裡

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved