为什么自己实现的getter需要保留并自动释放返回的对象?

12

示例:

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

设置器实际上已经保留了它,对吧?而且实际上没有人应该绕过Setter... 所以我想知道为什么getter不只是返回对象呢?它实际上已经被保留了。或者这只是在另一个对象在此期间传递到setter的情况下才需要吗?

4个回答

12

从这里 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将破坏现有的客户端代码...


7

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

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

这看起来很合理,但是如果setter和getter都没有使用autorelease,则“继续进行某些操作”部分很可能会崩溃,因为oldValue现在已被释放(假设没有其他人保留它)。您通常希望使用苹果公司的访问器方法示例中的技术1或技术2,以使像上面的代码一样的代码能够按照大多数人的预期工作。


4

比较以下代码

  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。仅仅因为您的变量中有一个对象的引用,并不能保证您不会在其下面得到它的dealloc。您必须对其进行保留,但 autorelease 可以让您避免这样做。除非您处理具有许多迭代的属性或循环(即使出现问题也可能不需要),否则您应该始终使用autorelease。


还可以在这里查看我的问题:http://stackoverflow.com/questions/3816898/methods-that-call-methods-basics-of-autorelease - Dan Rosenstark

0
我以前没有见过这种模式,但是对我来说似乎毫无意义。我猜想它的目的是在客户端代码调用父对象的“release”方法时保持返回值的安全性。它并不会真正造成什么影响,但我怀疑在设计良好的库中这种情况并不经常出现。

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

- (NSString *) value
{
    return myValue;
}

- (void) setValue: (NSString *) newValue
{
    if (newValue != myValue)
    {
       [myValue autorelease]; // actually, I nearly always use 'release' here
       myValue = [newValue retain];
    }
}

那要看情况。例如,对于可能从多个线程访问的属性,这是绝对必需的。更一般地说,很少遇到的情况往往会导致非常恼人的问题。 - Jens Ayton
我认为我明白你的意思,关于多线程,因为这样你可以拥有多个独立的释放池和运行循环。在这种情况下,我仍然认为在setter中使用autorelease更有意义。 - Mark Bessey
在多线程访问的情况下,我通常使用[obj copy]——拥有单独的对象实例可以消除任何冲突的可能性。 - Mark Bessey
如果你使用 [obj copy],那么你会在发送回来的 obj 上多一个 retain。谁会释放它呢?所以你最终会做 [[obj copy] autorelease],这是一样的。只返回 myValue(老派版本)是安全的,直到它不安全,就像海平面发电机。 - Dan Rosenstark

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