让我们从retain和release开始; autorelease 只是了解基本概念后的特例。
在 Cocoa 中,每个对象都会跟踪它被引用的次数(具体来说,NSObject 基类实现了这一点)。通过在一个对象上调用retain,你是在告诉它你想将它的引用计数加一。通过调用release,您告诉对象您正在释放它,并且它的引用计数递减。如果在调用release 之后,引用计数现在为零,则系统会释放该对象的内存。
这与malloc 和free 的基本不同之处在于,任何给定对象都不需要担心系统的其他部分崩溃,因为您已经释放了它们正在使用的内存。假设每个人都在按照规则进行保留/释放,当一段代码保留然后释放对象时,任何其他引用该对象的代码都不会受到影响。
有时可能令人困惑的是知道在什么情况下您应该致电retain 和release。我的一般经验法则是,如果我想在某个对象上挂起一段时间(例如,如果它是类中的成员变量),那么我需要确保对象的引用计数知道我。如上所述,对象的引用计数通过调用retain 递增。按照惯例,当使用“init”方法创建对象时,它也会增加(实际上设置为 1)。在这两种情况下,我有责任在完成后调用对象上的release。如果我不这样做,就会出现内存泄漏。
对象创建示例:
NSString* s = [[NSString alloc] init]; // Ref count is 1
[s retain]; // Ref count is 2 - silly
// to do this after init
[s release]; // Ref count is back to 1
[s release]; // Ref count is 0, object is freed
现在为autorelease。自动释放被用作一种方便(有时是必要的)方法来告诉系统在一段时间后释放这个对象。从管道的角度来看,当调用autorelease 时,当前线程的NSAutoreleasePool 会收到调用警报。 NSAutoreleasePool 现在知道一旦它获得机会(在事件循环的当前迭代之后),它可以在对象上调用release。从我们程序员的角度来看,它会为我们调用release,所以我们不必(事实上,我们不应该)。
需要注意的重要一点是(同样,按照惯例)所有对象创建 class 方法都会返回一个自动释放的对象。例如,在下面的例子中,变量“s”的引用计数为 1,但在事件循环完成后,它将被销毁。
NSString* s = [NSString stringWithString:@"Hello World"];
如果你想挂在那个字符串上,你需要显式调用retain,然后在完成后显式调用release。
考虑以下(非常人为的)代码,您会看到需要autorelease 的情况:
- (NSString*)createHelloWorldString
{
NSString* s = [[NSString alloc] initWithString:@"Hello World"];
// Now what? We want to return s, but we've upped its reference count.
// The caller shouldn't be responsible for releasing it, since we're the
// ones that created it. If we call release, however, the reference
// count will hit zero and bad memory will be returned to the caller.
// The answer is to call autorelease before returning the string. By
// explicitly calling autorelease, we pass the responsibility for
// releasing the string on to the thread's NSAutoreleasePool, which will
// happen at some later time. The consequence is that the returned string
// will still be valid for the caller of this function.
return [s autorelease];
}
我意识到这一切有点令人困惑——不过,在某些时候,它会点击。这里有一些参考资料可以帮助您: