在引用计数环境下用于内存管理的基本模型,是由 NSObject protocol
中所定义的一些方法,结合一套标准的方法命名约定共同提供的。NSObject 类还定义了一个dealloc
方法,该方法会在对象被释放时自动调用。本文将讲解在 Cocoa 编程中所需了解的关于正确进行内存管理的基本规则,并提供一些正确用法的示例。
内存管理的基本规则
对象管理模型基于对象所有权。每个对象都有一或多个持有者(owner
)。只要某对象还有一个持有者,它就不会被释放掉。如果一个对象没有了持有者,运行时系统(runtime system
)就会自动将其销毁掉。为了确保让你清晰地意识到何时持有了一个对象,Cocoa 采用了如下策略:
-
只要是你创建的对象,你就拥有其所有权
使用名字以alloc
,new
,copy
或者mutableCopy
开头的方法所创建对象(比如,alloc
,newObject
, 或者mutableCopy
)。 -
使用
retain
来获取对象的所有权
一个获取到的对象通常要确保它在被接收的方法内是始终有效的,并且该方法也要确保安全得将该对象返回给它的调用者。使用retain
有如下两个场景:
(1) 在存取器(accessor
)方法或init
方法的实现中,拿到一个想要存储为属性值的对象的所有权;
(2) 防止一个对象因其他操作的副作用而变成无效的(就像在 要避免对象在使用过程中被释放 中所阐述的那样)。 -
当不再需要一个对象时,必须要放弃对它的所有权
通过给一个对象发送release
消息或者autorelease
消息来放弃持有它。在 Cocoa 的惯用语中,放弃对一个对象的所有权通常被称为释放(release)一个对象。 -
如果不是某对象的持有者,不要执行放弃其所有权的操作
这只是前边几条策略规则的一个推论,在这里明确阐述下。
一个简单的例子
为了举例说明上述策略,思考如下代码片段:
1 2 3 4 5 6 7 |
|
对象Person
是由alloc
方法创建的,所以随后在不再需要它时向它发送了release
消息。这里的name
不是通过附带所有权的方法(the owning methods
)取到的,所以不必向其发送release
消息。值得注意的是,本例中使用了release
而不是autorelease
。
使用 autorelease 延迟发送 release 消息
当需要延迟发送release
消息时,使用autorelease
,特别是在要从一个方法中返回一个对象时。比如,可以像这样实现上例中的fullName
方法:
1 2 3 4 5 |
|
你将持有这里返回的通过alloc
创建的string
。按照内存管理的规则,在你丢掉对string
的引用之前,必须先放弃对它的所有权。然而,这里如果使用release
,string
将会在被返回前就被释放掉(该方法将返回一个无效的对象)。使用autorelease
,表示你想要放弃其所有权,但允许该方法的调用者在这里返回的string
被释放前使用它。
fullName
方法的实现也可以像这样:
1 2 3 4 5 |
|
根据上述的基本规则,这里你会持有通过stringWithFormat:
返回的string
,所以可以放心得从该方法中返回string
。
作为对比,如下实现是错误的:
1 2 3 4 5 |
|
依据上述命名约定,fullName
方法的调用者并不认为自己持有了这里所返回的string
,因此它也就没有理由释放所返回的string
,于是,这里就导致了内存泄漏。
你不具有通过引用(reference
)返回的对象的所有权
Cocoa 中的某些方法会指定通过引用(reference
)返回一个对象(也就是说,它们接受一个 ClassName **
或id *
类型的参数)。一个常见的例子是,使用NSError
对象来包含某个可能出现的 error 的相关信息。比如,initWithContentsOfURL:options:error:
(NSData) 和 initWithContentsOfFile:encoding:error:
(NSString)。
前边所述的规则同样适用于这种情况。当调用上述方法时,由于你并没有创建NSError
对象,也就不具有其所有权,因此也就没义务释放它,就像这个例子:
1 2 3 4 5 6 7 8 9 |
|
实现 dealloc 方法以释放所持有的对象
在NSObject
类中有一个dealloc
方法,它将在一个对象没有任何持有者时被自动调用,该对象所占内存也将被回收 – 在 Cocoa 的惯用语中这被称为freed
或者deallocated
(释放)。dealloc
方法所扮演的角色是,释放对象所占用的内存,处理它所占用的资源,包括对象的所有实例变量的所有权。
下面以Person
类为例来说明下其dealloc
方法的一种实现方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
重要:
(1) 千万不要直接调用另一个类的dealloc
方法;
(2) 必须在所实现的dealloc
方法的末尾调用父类(superclass
)的dealloc
方法;
(3) 不要把对系统资源的管理纳入对象的生命周期 (详情参考 不要在 dealloc 方法中管理稀缺资源 );
(4) 当应用终止后,对象的dealloc
方法可能并没有被调用。由于程序所占用的内存会在其终止时被自动清理掉,所以只需让操作系统去清理其所占用的资源就行,这会比挨个儿调用那些内存管理方法更高效。
Core Foundation 使用类似的规则
Core Foundation 对象的内存管理规则与此类似 (详情参阅 Memory Management Programming Guide for Core Foundation )。然而,Cocoa 与 Core Foundation 的命名约定有所不同。需要注意,Core Foundation 的创建规则 (参考 The Create Rule )并不适用于返回 Objective-C 对象的方法。例如,在如下所示的代码片段中,这里不用负责释放myInstance
的内存:
1
|
|