这是客观的内存泄漏吗?

时间:2009-04-16 16:19:45

标签: objective-c cocoa memory-leaks

我知道如果你做了以下事情,你肯定会有内存泄漏:

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

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

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

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

7 个答案:

答案 0 :(得分:4)

self.foo = nil会转化为

[nil retain]
[foo release]
foo = nil

此处没有内存泄漏。

答案 1 :(得分:2)

不,第二个例子不是内存泄漏。实际上,这就是我在retain方法中处理dealloc属性的方式。它只是更清洁了。

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

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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

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的所有修改都通过设置器。

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

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

答案 4 :(得分:1)

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

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

如果foo是一个实例变量,但foo属性实际上是由另一个实例变量支持,或者根本没有实例变量,那么(a)你写的是难以维护的代码和(b)可能是泄密。

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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

我假设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;