我刚刚开始了解Objective-C和Cocoa,以期使用iPhone SDK.我对C的malloc
和free
概念相当满意,但Cocoa的参考计数方案让我相当困惑.有人告诉我,一旦你理解了它,它就非常优雅了,但我只是还没有走出低谷.
release
、retain
和autorelease
是如何工作的?它们的使用有哪些约定?
(或者,如果没有做到这一点,你读了什么帮助你得到了它?)
我刚刚开始了解Objective-C和Cocoa,以期使用iPhone SDK.我对C的malloc
和free
概念相当满意,但Cocoa的参考计数方案让我相当困惑.有人告诉我,一旦你理解了它,它就非常优雅了,但我只是还没有走出低谷.
release
、retain
和autorelease
是如何工作的?它们的使用有哪些约定?
(或者,如果没有做到这一点,你读了什么帮助你得到了它?)
让我们从retain
和release
开始;一旦你理解了基本概念,autorelease
实际上只是一个特例.
在Cocoa中,每个对象都跟踪它被引用的次数(具体来说,NSObject
基类实现了这一点).通过对一个对象调用retain
,您告诉它您想将其引用计数增加1.通过拨打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
.自动释放被用作一种方便(有时是必要的)方式,告诉系统在一段时间后释放此对象.从管道Angular 来看,当调用autorelease
时,当前线程的NSAutoreleasePool
将收到调用的alert .NSAutoreleasePool
现在知道,一旦获得机会(在事件循环的当前迭代之后),它就可以对对象调用release
.从我们作为程序员的Angular 来看,它负责为我们拨打release
,所以我们不必(事实上,我们不应该).
需要注意的是(按照惯例),所有对象创建方法都会返回一个自动删除的对象.例如,在下面的示例中,变量"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];
}
我意识到所有这些都有点令人困惑——不过,在某个时候,它会发出咔哒声.以下是一些参考资料,可以帮助您继续: