Advanced Memory Management Programming Guide 高级内存管理编程指南(官方文档翻译)[iOS]

Advanced Memory Management Programming Guide - 高级内存管理编程指南(官方文档翻译)

版权声明:本文为博主原创翻译,如需转载请注明出处。

新博客文章地址:Advanced Memory Management Programming Guide 高级内存管理编程指南(官方文档翻译)
CSDN文章地址:Advanced Memory Management Programming Guide 高级内存管理编程指南(官方文档翻译)

苹果源文档地址 - 点击这里

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,你要确保你在内存中没有比实际需要 更多的对象。



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的应用程序提供了内存管理的两种方法。

  1. 在本指南中说明的方法,通过跟踪你自己的对象,明确地管理内存,被称为“手动保留释放(manual retain-release)”或MRR。这是用一个模型来实现,称为引用计数,是基础类 NSObject 提供的,并与运行环境相结合。
  2. 在自动引用计数,或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”(例如,allocnewObjectmutableCopy )这种类型开头的方法来创建一个对象。

  • 可以使用 retain 保留对象的所有权

接收到的对象通常是保证其接收的方法中仍然有效,而且方法也可以安全地将对象返回到它的调用。在两种情况下您可以使用 retain ,(1)在存取方法或 init方法的实现中,你希望将得到的返回对象作为成员变量(property)来存储。(2)在执行某些操作时,为了防止对象被废止。(例如 Avoid Causing Deallocation of Objects You’re Using

  • 当你不再需要它,你必须放弃你自己的对象的所有权

您可以通过发送一个 release 消息或 autorelease 消息放弃对象的所有权。在Cocoa 术语中,所谓 放弃所有权,就是 release 一个对象。

  • 对于你不拥有的对象,不要尝试放弃所有权

这是上面的策略规则的推论,明确表示。

A Simple Example - 一个简单的例子

为了说明这个策略,可以考虑下面的代码片段:

1
2
3
4
5
6
7
{
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这样的方法:

1
2
3
4
5
- (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的方法:

1
2
3
4
5
- (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 开始),这样你就可以放心地从该方法返回字符串。

通过对比的方式,下面的实施是错误的 :

1
2
3
4
5
- (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 对象,所以你没有拥有它。 因此,没有必要将它释放,如本例所示:

1
2
3
4
5
6
7
8
9
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 或者 deallocateddealloc方法的作用就是释放对象自身的内存,以及处置已持有的任何资源,包括任何对象的实例变量的所有权。

下面的例子说明了如何实现一个Person类的dealloc方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@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 的所有权:

1
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.

如果你的类有一个属性是一个对象,你必须确保,当你使用它,被设置为值的任何对象都不释放。 因此,当它被设置时,必须声明对象的所有权。你还必须保证对这些对象 所有权的放弃。

有时候似乎很麻烦,如果你坚持用 getset 这种方法方法来实现,那么内存管理的问题出现几率就大幅度减少了。如果对于整个代码的实例变量,使用的是retainrelease,你几乎可以肯定是在做错误的事情。

1
2
3
@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实例变量,所以没有必要retainrelease

1
2
3
- (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。

1
2
3
4
5
6
- (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

1
2
3
4
5
- (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 对象。 因此,不需要 retainrelease 消息

1
2
3
4
- (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,或者如果实例变量的内存管理发生了变化)。

1
2
3
4
5
- (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,你可以实现一个初始化方法如下:

1
2
3
4
5
6
7
- 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:的方法:

1
2
3
4
5
6
7
- 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消息放弃任何实例变量的所有权,最终也应该调用父类的实现:

1
2
3
4
- (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.

  1. 当一个对象从 collection classes 中删除的时候。
1
2
3
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.

  1. 当一个“父对象”被释放。
1
2
3
4
5
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。例如:

1
2
3
4
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会取得该对象的所有权。当对象从集合中删除或当集合本身释放时,集合将放弃所有权。因此,如果你想创建数字数组,可以像下面这样做:

1
2
3
4
5
6
7
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 ),因为数组会这么做。

1
2
3
4
5
6
7
8
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 ,如下面的示例所示:

1
2
3
@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:

像任何其他代码块,自动释放池块可以嵌套:

1
2
3
4
5
6
7
@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 会记录适当的错误信息)AppKitUIKit 框架处理每个事件循环迭代(如鼠标按下事件或触摸)都在自动释放池块中。因此,你一般不必创建一个自动释放池,甚至不需要知道创建 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 循环中使用本地自动释放池块

1
2
3
4
5
6
7
8
9
10
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, 如本例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
– (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 是否是多线程模式。

文章目录
  1. 1. About Memory Management - 关于内存管理
    1. 1.1. At a Glance - 简单了解
      1. 1.1.1. Good Practices Prevent Memory-Related Problems - 好的做法,防止内存相关的问题
      2. 1.1.2. Use Analysis Tools to Debug Memory Problems - 使用分析工具来调试内存问题
  2. 2. Memory Management Policy - 内存管理策略
    1. 2.1. Basic Memory Management Rules - 基本内存管理规则
      1. 2.1.1. A Simple Example - 一个简单的例子
      2. 2.1.2. Use autorelease to Send a Deferred release - 使用autorelease发送延迟release
      3. 2.1.3. You Don’t Own Objects Returned by Reference - 你没有通过引用返回的对象的所有权
    2. 2.2. Implement dealloc to Relinquish Ownership of Objects - 实现dealloc放弃对象的所有权
    3. 2.3. Core Foundation Uses Similar but Different Rules - Core Foundation使用了类似但不同的规则
  3. 3. 内存管理实践
    1. 3.1. Use Accessor Methods to Make Memory Management Easier - 使用访问器方法使内存管理更容易
      1. 3.1.1. Use Accessor Methods to Set Property Values - 使用访问器方法来设置属性值
      2. 3.1.2. Don’t Use Accessor Methods in Initializer Methods and dealloc - 不要在初始化方法和 dealloc中使用访问器方法
    2. 3.2. Use Weak References to Avoid Retain Cycles - 使用弱引用来避免所有权的死锁
    3. 3.3. Avoid Causing Deallocation of Objects You’re Using - 避免你正在使用的对象被 dealloc
    4. 3.4. Don’t Use dealloc to Manage Scarce Resources - 不要使用 dealloc 来管理关键系统资源
    5. 3.5. Collections Own the Objects They Contain - Collections 拥有他们所包含的对象
    6. 3.6. Ownership Policy Is Implemented Using Retain Counts - 通过引用计数实现所有权策略
  4. 4. Using Autorelease Pool Blocks - 使用自动释放池块
    1. 4.1. About Autorelease Pool Blocks - 关于自动释放池块
    2. 4.2. Use Local Autorelease Pool Blocks to Reduce Peak Memory Footprint - 使用本地自动释放池块,降低峰值内存占用
    3. 4.3. Autorelease Pool Blocks and Threads - 自动释放池块和线程