引用计数

Cocoa 采用引用计数技术管理对象。

  • 当使用 alloc、new 方法或通过 copy 消息(生成接收对象的一个副本)创建一个对象时,对象的保留计数器值被设置为 1 。
  • 要增加对象的保留计数器值,可以给对象发送一条 retain 消息。
  • 要减少对象的保留计数器值,可以给对象发送一条 release 消息。
  • 要获得保留计数器的当前值,可以发送 retainCount 消息。

1
2
3
- (id) retain;
- (void) release;
- (unsigned) retainCount;

当一个对象因保留计数器归 0 而即将被销毁时,Objective-C 自动向对象发送一条 dealloc 消息。你可以在自己的对象中重写 dealloc 方法。可以通过这种方法释放已经分配的全部相关资源。一定不要直接调用 dealloc 方法。可以利用 Objective-C 在需要销毁对象时调用 dealloc 方法。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@interface RetainTracker : NSObject
@end // RetainTracker
@implementation RetainTracker
- (id) init
{
if (self = [super init]) {
NSLog (@"init: Retain count of %d.", [self retainCount]);
}
return (self);
} // init
- (void) dealloc
{
NSLog (@"dealloc called. Bye Bye.");
[super dealloc];
} // dealloc
@end // RetainTracker
int main (int argc, const char *argv[])
{
RetainTracker *tracker = [RetainTracker new];
// count: 1
[tracker retain]; // count: 2
NSLog (@"%d", [tracker retainCount]);
[tracker retain]; // count: 3
NSLog (@"%d", [tracker retainCount);
[tracker release]; // count: 2
NSLog (@"%d", [tracker retainCount);
[tracker release]; // count: 1
NSLog (@"%d", [tracker retainCount);
[tracker retain]; // count: 2
NSLog (@"%d", [tracker retainCount);
[tracker release]; // count: 1
NSLog (@"%d", [tracker retainCount);
[tracker release]; // count: 0, dealloc it
return (0);
} // main

对象所有权

  • 如果某个实体拥有一个对象,则该实体要负责确保对其拥有的对象进行清理。

  • 这个实体可以是另一个对象,或者一个函数。例如,在 Car 类中, car 对象拥有其指向的 engine 和 tire 对象。又比如,main() 函数创建了一个新的 car 对象,因此称 main() 函数拥有 car 对象。

  • 多个实体可以同时拥有同一个特定对象。此时要格外注意考虑对象的所有权关系。

自动释放池

Cocoa 中有一个自动释放池(autorelease pool)的概念。顾名思义,它是一个存放实体的池(集合),这些实体可能是对象,能够被自动释放。

NSObject 类提供了一个 autorelease 方法:

1
- (id) autorelease;

当给一个对象发送 autorelease 消息时,实际上是将该对象添加到 NSAutoreleasePool 中。当自动释放池被销毁时,会向该池中的所有对象发送 release 消息。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init];
RetainTracker *tracker;
tracker = [RetainTracker new]; // count: 1
[tracker retain]; // count: 2
[tracker autorelease]; // count: still 2
[tracker release]; // count: 1
NSLog (@"releasing pool");
[pool release];
// gets nuked, sends release to tracker
return (0);
} // main

内存管理规则

  • 如果使用 new、 alloc 或 copy 操作获得一个对象,则该对象的保留计数器值为 1 。当不再使用该对象时,你要负责向该对象发送一条 release 或 autorelease 消息。这样,该对象在其使用寿命结束时被销毁。
  • 如果通过任何其他方法获得一个对象时,则假设该对象的保留计数器值为 1 ,而且已经被设置为自动释放。你不需要执行任何操作来确保该对象被清理。
  • 如果保留了某个对象,则必须保持 retain 方法和 release 方法的使用次数相等。

垃圾回收

Objective-C 2.0 引入了垃圾回收机制。对于已经创建和使用的对象,当你忘记清理它们时,系统会自动识别哪些对象仍在使用,哪些对象可以回收。启用垃圾回收非常简单,只不过这是一种可选择启用的功能。只需转到项目信息窗口的 Build 选项卡,然后选择 Required [-fobjc-gc-only] 选项即可。

启用垃圾回收以后,通常的内存管理命令全都变成了空操作指令,不执行任何操作。

垃圾回收器定期检查变量和对象以及它们之间的指针,当发现没有任何变量指向某个对象时,就将该对象视为应该被丢弃的垃圾。因此,如果你在一个实例变量中指向某个对象,一定要在某个时候将该实例变量赋值为 nil ,以取消对该对象的引用并使垃圾回收器知道该对象可以被清理了。

注意:如果开发 iPhone 软件,则不能使用垃圾回收。实际上,苹果公司建议 iPhone 开发者不要在自己的代码中使用 autorelease 方法,同时还要避免使用创建自动释放对象的便利函数。