Objective-C已经有了垃圾回收机制,但是我们最关心的iso不支持垃圾回收,所以,让我们冷静一下,抛开垃圾回收,自己来管理内存吧。
对象所有权:当一个对象的属性是另外一个对象的时候,换言之,对象A中,有一个属性是对象B,那么,对象A对于对象B,就有对象所有权。那么,在对象A释放的时候,我们必须确保对象B的引用计数减少1(这里不一定是释放对象B,因为对象B还可能在其他地方被引用)。
一个非常常见的例子就是set方法,把一个对象设置为另外一个对象的属性。在这里,我们必须确保引用计数不混乱,一个良好的实现如下:
//这里还没有解决Car释放的时候,Engine跟着释放
-(void) setEngine:(Engine *) newEngine
{
[newEngine retain];//增加newEngine的引用计数,标示着Car对newEngine的对象所有权
[engine release];//释放旧的engine
engine = newEngine;//引用新的newEngine
}
自动释放池:一个容器,可以装载对象,当池销毁的时候,同时给每一个对象发送release消息(注意这里是发送release消息,而不是直接销毁里面的对象)。当给一个对象发送autorelease消息时,实际上是将该对象添加到NSAutorelease中。当自动释放池被销毁时,会给该对象发送release消息。当然,自动释放池也是一个正常的Objective-C对象,它也遵守引用计数规则。下面有两个例子,可以比较清晰的展示自动释放池的工作现象。
#import <Foundation/Foundation.h>
@interface RetainTracker : NSObject
@end
@implementation RetainTracker
- (id) init
{
if (self = [super init]) {
NSLog (@"init: Retain count of %d.",
[self retainCount]);
}
return (self);
}
- (void) dealloc
{
NSLog (@"dealloc called. Bye Bye.");
[super dealloc];
}
@end
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加入到自动释放池中
[tracker autorelease]; // count: still 2
[tracker release]; // count: 1
NSLog (@"releasing pool");
[pool release];
return (0);
}
#import <Foundation/Foundation.h>
@interface RetainTracker : NSObject
@end
@implementation RetainTracker
- (id) init
{
if (self = [super init]) {
NSLog (@"init: Retain count of %d.",
[self retainCount]);
}
return (self);
}
- (void) dealloc
{
NSLog (@"dealloc called. Bye Bye.");
[super dealloc];
}
@end
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 retain]; // count: 3
//把tracker加入到自动释放池中
[tracker autorelease]; // count: still 3
[tracker release]; // count: 2
NSLog (@"releasing pool");
[pool release];
return (0);
}
请读者自行比较两段代码的输出。
Cocoa内存管理规则:
1.当你使用new、alloc、copy方法创建一个对象时,该对象的保留计数器值为1。当不再使用该对象的时候,你要负责向该对象发送一条release或autorelease消息。这样,该对象将在其使用寿命结束时被摧毁。
2.当你通过任何其他方法获得一个对象时,你不需要执行任何操作来确保该对象被清理。就比如这样的语句:NSMutableArray *array = [NSMuableArray arrayWithCapacity:17];你不需要维护array的引用计数,系统会帮你完成(借助于自动释放池)。当然,如果你打算在一段时间内拥有array,你需要给它增加引用计数,并且在不使用的时候减少引用计数。
3.如果你保留了某个对象,你需要释放或者自动释放该对象。必须保持retain方法和release方法的使用次数相等(就必须开始讨论的set方法)。
最后说明两个问题:
1.开始的set方法并没有确保对象销毁时,它的属性对象也销毁,处理的方式很简单,在对象的dealloc方法中,给属性对象发送release。
2.自动释放池也不是可以乱用的,比如在循环中,我们不断的新建对象,而且对象被加载到自动释放池中。那么,当循环很大的时候,被加载到自动释放池的对象并没有被释放,我们的程序占用的内存会持续增长。这时候的处理方法是不断的创建自己的自动释放池,然后释放(释放的时候,里面的对象也跟着释放了),再创建,再释放。