【问题标题】:Why should a self-implemented getter retain and autorelease the returned object?为什么自我实现的 getter 应该保留并自动释放返回的对象?
【发布时间】:2023-04-05 04:42:01
【问题描述】:

例子:

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

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

【问题讨论】:

    标签: iphone cocoa-touch memory-management uikit


    【解决方案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,除非您正在处理具有多次迭代的属性或循环(除非出现问题,否则可能甚至不使用)。

        【讨论】:

        【解决方案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(老派版本)是安全的,直到它不安全,就像海平面发电机一样。
        猜你喜欢
        • 2010-12-16
        • 1970-01-01
        • 2013-12-27
        • 2011-03-01
        • 2011-10-27
        • 2011-07-01
        • 1970-01-01
        • 2011-09-19
        • 1970-01-01
        相关资源
        最近更新 更多