【问题标题】:Is this an objective-c memory leak?这是一个objective-c内存泄漏吗?
【发布时间】:2010-10-19 21:43:59
【问题描述】:

我知道,如果您执行以下操作,您肯定会发生内存泄漏:

id foo = [[NSObject alloc] init];
foo = nil;

但是,如果你使用 self.foo,一个带有保留的属性呢?而您的代码如下所示:

foo = [[NSObject alloc] init];
self.foo = nil;

这仍然是内存泄漏,因为访问器在将内存设置为 nil 之前先释放内存吗?

【问题讨论】:

    标签: objective-c cocoa memory-leaks


    【解决方案1】:

    因为似乎没有其他人注意到:可能有泄漏。

    我假设 foo 既是 ivar 又是 retain 属性:

    @interface Foo : NSObject {
      NSObject * foo;
    }
    @property (nonatomic, retain) NSObject * foo;
    @end
    

    假设您的代码如下所示:

    -(void)bar {
      foo = [[NSObject alloc] init];
      self.foo = nil;
    }
    

    这本身不会泄漏如果foo 是零开始的。这并不意味着它不会泄漏——假设您添加了更多代码:

    -(void)baz {
      self.foo = [[NSObject new] autorelease];
    }
    
    -(void)fubar {
      [self baz];
      [self bar];
    }
    

    foo = [[Foo alloc] init] 之类的东西在init 方法中通常是安全的,因为假设您只调用其中一个,所以foo 最初保证为nil。在其他任何地方,您都必须更加小心。

    // Use assertions so it crashes debug builds if it's already set
    assert(!foo);
    foo = [[NSObject alloc] init];
    self.foo = nil;
    
    // Or release explicitly.
    [foo release];
    foo = [[NSObject alloc] init];
    self.foo = nil;
    
    // Or just use the setter, which will do the "right thing".
    // This is particularly relevant for "copy" or "assign" accessors.
    self.foo = [[[NSObject alloc] init] autorelease];
    self.foo = nil;
    

    【讨论】:

      【解决方案2】:

      到目前为止,所有答案都假定第二个示例第一行中的“foo”是foo 属性后面的实例变量。这是默认行为。

      如果第一行赋值给的foo是一个局部变量,那么foo属性是无关紧要的,除非你稍后在方法中释放它,否则你将泄漏该对象。

      如果foo 是一个实例变量,但foo 属性实际上由不同的实例变量支持,或者根本没有实例变量,那么 (a) 您正在编写难以维护的代码并且 (b ) 这可能是泄漏。

      最后,回应前面的答案:如果foo 是支持foo 属性的实例变量,那么这不是泄漏,因为您在第二行调用的setFoo: 方法将释放对象你在第一行输入了foo 实例变量。

      【讨论】:

        【解决方案3】:

        不,没有内存泄漏。第二个示例中的代码在逻辑上等同于

        foo = [[NSObject alloc] init];
        [nil retain];
        [foo release];
        foo = nil;
        

        因为@synthesized setter 在逻辑上等价于

        - (void)setFoo:(id)newFoo {
          [newFoo retain];
          [foo release];
          foo = newFoo;
        }
        

        值得注意的是,直接设置foo 可能不是您想要在init 方法之外执行的操作。如果您直接为foo 赋值,则绕过自动KVO 通知(您必须将分配包装在willChangeValueForKey:/didChangeValueForKey: 对中)并且如果它覆盖setFoo: 方法,期望对 foo 的所有修改都通过 setter。

        您在 init 方法中直接分配给 foo,因为 setFoo: 方法或子类的覆盖 setFoo: 方法可能有副作用或取决于实例的完全初始化。

        同样,出于同样的原因,您将在 -dealloc 方法中使用 [foo release] 而不是 self.foo = nil;

        【讨论】:

          【解决方案4】:

          不,第二个例子不是内存泄漏。事实上,这就是我在dealloc 方法中处理retain 属性的方式。就是干净多了。

          你唯一需要注意的是确保不要写

          self.foo = [[NSObject alloc] init];
          

          否则您将双重保留该对象并最终导致内存泄漏。

          【讨论】:

          • 请记住,如果访问器方法更改为修改另一个 ivar(可能已经发布),通常不鼓励在 dealloc 中使用属性。我只是用 release 代替。
          • @Alex:+1 表示非常好的警告,但 -1 表示建议在 dealloc 方法中使用 self.foo = nil!
          • @eJames self.foo = nil 是 Apple 最初建议在 dealloc 方法中执行此操作的方式,并且在其示例代码中随处可见。
          • 我认为对于普通的@synthesize 访问器,属性语法是要走的路。对苹果来说已经足够了!我的想法是,为什么要花两行代码来做你可以做的事情?
          • Obj-C 2 中的属性看起来不错,但它们增加了复杂性——尤其是当您可以混合和匹配时。我认为他们应该走得更远。也许我们将在 15 年后通过 ObjC 3 实现这一目标。
          【解决方案5】:

          属性使您的代码看起来像赋值,但实际上它们与您可能在 Obj-C 2.0 之前自己编写的传统访问器方法相同。使用属性 O​​bj-C 只是在幕后为您生成访问器方法,而不是使用您在声明中指定的关键字(假设您使用 @synthesize 并且无论如何都不编写自己的访问器方法)。

          【讨论】:

            【解决方案6】:

            我不这么认为,因为通过 self.foo = nil 您实际上是在使用 setter 并免费获得内存管理。

            【讨论】:

              【解决方案7】:

              self.foo = nil 会翻译成

              [nil retain]
              [foo release]
              foo = nil
              

              这里没有内存泄漏。

              【讨论】:

                猜你喜欢
                • 2011-03-29
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-04-03
                • 2011-02-20
                • 2013-08-12
                相关资源
                最近更新 更多