返回对象的保留/释放

7
我对Objective-C还很陌生,所以这可能是一个愚蠢的问题。我不禁注意到ObjC和Microsoft的COM在内存管理方面有相似之处(AddRef/Release与retain/release)。在COM环境中,你几乎总是被强制在将对象返回给调用者之前添加AddRef(retain)。到目前为止,从我所见的(我已经阅读了《Cocoa® Programming for Mac® OS X (第三版)》的三分之一),内存管理部分有些模糊。假设没有GC,那么返回一个对象的惯用方式是什么?
5个回答

14

阅读 内存管理编程指南 了解自动释放池。

在 Objective-C 中,按照惯例,除非返回对象的方法名以“alloc”、“new”、“copy”或“mutableCopy”开头,否则应该返回自动释放的对象。自动释放的对象由 Objective-C 在一个池中跟踪并自动处理,这意味着你不需要关心向它们发送最终释放的操作。与 COM 相比,这极大地简化了引用计数,因此你大部分时间都看不到返回对象的 release 调用。相反,同样的惯例指定了所有由名称以 alloc、new、copy 或 mutableCopy 开头的方法返回的对象是方法调用者的责任。你必须手动对这些对象调用 release,否则你的程序将会有内存泄漏问题。


在你的方法末尾不要盲目添加return [objectName autorelease];。在这样做之前,请确保您没有将autorelease发送到已经在其他地方被自动释放的对象上并返回它。如果这样做,很可能会导致您的应用程序随机崩溃。至少这是我的经验。 - RobK

9

Cocoa通过引入第三个兄弟姐妹autorelease,绕过了COM中AddRef/Release方法的限制。

  • retain - 我需要这个,让它留下来。
  • release - 我不再需要这个,你可以立即删除它。
  • autorelease - 我不需要这个,但还是让它保留几秒钟,以便其他人先捡起来。

这个小小的补充使得大多数返回值可以被处理,就像我们拥有垃圾回收一样。如果你不想保留返回值,就无需额外操作。

为了让这个功能正常工作,有一个约定(一个足够好的约定,可以让编译器自动处理内存,使用即将推出的ARC):

  • 以以下名称开头的方法必须返回已保留的实例:
    • alloc
    • copy
    • new
    • retain
  • 所有其他方法必须返回已自动释放的实例。

以下是三个示例实现,展示了如何将其应用于实际情况:

-(NSString*)newHelloWorldString {
    NSString* s = [NSString stringWithString:@"Hello world"];
    // Apply retain because s in now autoreleased
    return [s retain];
}

-(NSString*)helloWorldString {
    NSString* s = [[NSString alloc] initWithString:@"Hello world"];
    // Apply autorelease because s is now retained.
    return [s autorelease];
}

-(NSString*)fullName {
    // No memory management needed, everything is autoreleased and good.
    NSString* fn = [self firstName];
    NSString* ln = [self lastName];
    NSString* s = [NSString stringWithFormat:@"%@ %@", fn, ln];
    return s;
}

1
通常是这样的:
返回 [object autorelease];
你可以在另一端保留。
如果您计划部署在Lion/iOS5上或使用最新的SDK,则还应该查看ARC。

我刚开始尝试使用XCode,这只是我的业余爱好,所以在不久的将来我不会部署任何东西:D。如果可以问一下,什么是ARC? - Jörgen Sigvardsson
这是Cocoa库返回它们的对象的方式吗? - Jörgen Sigvardsson
ARC = 自动引用计数。具有构建步骤,根据约定的模式自动插入保留/释放。 - Thilo
通常情况下,您应该返回一个自动释放的对象。注意不要随意调用 return [object autorelease];,因为我很少会像这样构建代码结构。不要仅仅盲目地在返回值上打上自动释放标记;请阅读内存管理指南并理解您正在做的事情。 - kubi

-1

我建议让接收它的类保留它。即,类stackoverflow接收对象answer。

i.e

-(void) setAnswer:(Answer*) _answer{
    self.answer = _answer; // If the answer is created from a returned message.
    [_answer release];
}

编辑:我想我可能在第二次查看时放错了东西。我的意思是类似于:

Answer *_answer = [stackoverflow createAnswer];
self.answer = _answer;
[_answer release];

1
又一个糟糕的答案 XD。你不应该释放作为参数传递给方法的对象。请停止发布这样的答案以避免混淆新手。 - Altealice
请解释一下为什么您不能在这里发布它,有哪些问题可能会遇到。启发我一下。 - user281300
你只应该释放你创建的对象。传递给你方法的参数并不是在方法内部创建的,所以你不应该释放它们,否则当调用你方法的人释放(或者给它发送任何消息)他传递给你的参数时,会导致一个“向已释放实例发送消息”的错误。 - Altealice
但是self.answer将其增加了1,然后释放它,因此从本质上讲,该类成为所有者+1,因为发送者已经在那里有一个+1。 - user281300
没关系,那是完全不同的事情。问题在于你正在释放不应该释放的参数。请再次阅读内存管理指南。有很多你需要理解的事情。http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/MemoryMgmt.html - Altealice
显示剩余3条评论

-3

如果你返回一个对象,它的保留由所有者负责,我会尽可能避免使用自动释放,因为一旦 nspool 启动,这些对象就会消失,如果它们仍在使用中,将会引起问题。

例如:Answer *answer = [stackoverflow getAnswer],如果 answer 是在 getanswer 方法中创建的,那么检索它的人负责释放它。

有道理吗?


4
绝大多数方法应该返回自动释放对象。建议“尽可能避免使用自动释放池”是错误的建议。 - kubi
我仍然坚持我的说法,避免返回自动释放的对象。这在 iPhone 设计指南中已经被提到了无数次。例如,如果你从 CoreData 返回某个东西,你不应该只是返回一个自动释放的对象。它会一直停留在内存中,直到池触发,如果你没有在拥有者中正确地保留它,你会遇到内存问题,如果你那么做了,你就有了一个无所作为的自动释放对象... - user281300
这是糟糕的建议:它与所有传统的Objective-C/Cocoa代码完全相反。 - Aidan Steele
+1 kubi。@user281300,你没有抓住这个非常老的主题的重点...由于ARC,该主题可能早已过时。但是,(无论是Cocoa还是其他语言)Autorelease是一种编程便利,可以帮助人们不要忘记释放那些将要使用一段时间的东西。如果您在紧密/大循环中使用它们会导致内存池膨胀,苹果正在尝试帮助您避免您所担心的问题。 :-) - eric
是的,但我的观点是,你不应该无论何时都使用它。 - user281300
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接