为啥自我实现的 getter 应该保留并自动释放返回的对象?

Posted

技术标签:

【中文标题】为啥自我实现的 getter 应该保留并自动释放返回的对象?【英文标题】:Why should a self-implemented getter retain and autorelease the returned object?为什么自我实现的 getter 应该保留并自动释放返回的对象? 【发布时间】:2010-10-22 13:22:11 【问题描述】:

例子:

- (NSString*) title 
    return [[title retain] autorelease];

setter 实际上已经保留了它,对吧?实际上没有人应该绕过 Setter ......所以我想知道为什么 getter 不只是返回对象?它实际上已经保留了。或者如果同时另一个对象被传递给setter,是否需要这样做?

【问题讨论】:

【参考方案1】:

从这里http://www.macosxguru.net/article.php?story=20030713184140267

- (id)getMyInstance
    
        return myInstanceVar ;
    

- (id)getMyInstance

    return [[myInstanceVar retain] autorelease] ;

有什么区别? 第二个允许调用者获取容器对象的实例变量,处置容器并继续使用实例变量直到当前自动释放池的下一次释放,而不会因释放间接生成的实例变量而受到伤害通过释放其容器:

aLocalVar = [aContainer getAnInstanceVar] ;
[aContainer release];
doSomething(aLocalVar);

如果“get”是以第一种形式实现的,你应该写:

aLocalVar = [[aContainer getAnInstanceVar] retain];
[aContainer release];
doSomething(aLocalVar);
[aLovalVar release];

第一种形式在代码执行速度方面效率更高一些。 但是,如果您正在编写供他人使用的框架,也许应该推荐第二个版本:它使使用您的框架的人的生活更轻松:他们不必过多考虑自己在做什么……; ) 如果您选择第一个样式版本,请在文档中明确说明...无论您选择哪种方式,请记住从版本 1 更改为版本 2 是为了保存客户端代码,从版本 2 回到版本 1 会破坏现有客户端代码……

【讨论】:

【参考方案2】:

这不仅仅适用于某人释放容器的情况,因为在这种情况下,他们应该自己保留对象更为明显。考虑这段代码:

NSString* newValue = @"new";
NSString* oldValue = [foo someStringValue];
[foo setSomeStringValue:newValue];
// Go on to do something with oldValue

这看起来很合理,但是如果 setter 和 getter 都没有使用 autorelease,那么“继续做某事”部分可能会崩溃,因为 oldValue 现在已被释放(假设没有其他人保留它)。您通常希望使用来自Apple's accessor method examples 的技术 1 或技术 2,因此上述代码将按照大多数人的预期工作。

【讨论】:

【参考方案3】:

比较这段代码

  return [[title retain] release]; // releases immediately

有了这个

  return [[title retain] autorelease]; // releases at end of current run loop (or if autorelease pool is drained earlier)

第二个保证客户端将有一个未释放的对象可以使用。

这在这种情况下很有用(客户端代码):

 NSString *thing = [obj title];
 [obj setTitle:nil]; // here you could hit retainCount 0!
 NSLog(@"Length %d", [thing length]); // here thing might be dealloced already!

title 方法中的保留(以及使用autorelease 而不是release)可防止此代码崩溃。自动释放的对象不会调用其release 方法直到当前调用堆栈执行完毕(当前运行循环结束)。这使调用堆栈中的所有客户端代码都有机会使用此对象,而不必担心它会被解除分配。

要记住的重要事项: 这不是 Java、Ruby 或 PHP。仅仅因为你在你的 [原文] 变量中有一个对象的引用并不能确保你不会从你下面得到它。您必须保留它,但然后您必须记住释放它。自动释放可让您避免这种情况。您应该始终使用 autorelease,除非您正在处理具有多次迭代的属性或循环(除非出现问题,否则可能甚至不使用)。

【讨论】:

也可以在这里查看我的问题:***.com/questions/3816898/…【参考方案4】:

我以前从未见过这种模式,但对我来说似乎毫无意义。如果客户端代码在父对象上调用“释放”,我想这样做的目的是保持返回值的安全。它并没有真正伤害任何东西,但我怀疑这种情况经常出现在精心设计的库中。


啊,好的。从 smorgan 链接到的文档来看,这似乎是苹果目前推荐人们使用的方法之一。我想我还是更喜欢老式的版本:

- (NSString *) value

    return myValue;


- (void) setValue: (NSString *) newValue

    if (newValue != myValue)
    
       [myValue autorelease]; // actually, I nearly always use 'release' here
       myValue = [newValue retain];
    

【讨论】:

这取决于。例如,对于可以从多个线程访问的属性,这是绝对要求。更一般地说,不经常出现的情况会导致非常烦人的头痛。 我想我明白你在说什么,关于多线程,因为你可以有多个独立的发布池和运行循环。在这种情况下,我仍然认为 setter 中的 autorelease 更有意义。 在多线程访问的情况下,我通常使用 [obj copy] - 拥有单独的对象实例可以消除任何冲突的机会。 如果你使用[obj copy],那么你在你发回的obj上有一个额外的保留。谁来释放它?所以你最终会做[[obj copy] autorelease],这是一样的。只返回myValue(老派版本)是安全的,直到它不安全,就像海平面发电机一样。

以上是关于为啥自我实现的 getter 应该保留并自动释放返回的对象?的主要内容,如果未能解决你的问题,请参考以下文章

为啥保留/释放而不是新/删除?

我应该在重新分配之前调用分配给保留属性的自动释放对象的释放吗?

为啥我不能将自动实现的 getter 和 setter 与 List 一起使用? (统一,C#)

返回executeFetchRequest的NSArray:结果时我应该保留,自动释放还是啥都不做?

保留周期:为啥这是一件坏事?

保留“自我”的坏习惯?