简体   繁体   中英

Is this an objective-c memory leak?

I know that if you do the following you most certainly have a memory leak:

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

But, what if you're using self.foo, a property with retain? And your code instead looks like the following:

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

Is that still a memory leak since the accessor releases the memory first before setting it to nil?

self.foo = nil would translate into

[nil retain]
[foo release]
foo = nil

There is no memory leak here.

Nope, the second example is not a memory leak. In fact, that's how I deal with retain properties in my dealloc method. It's just a lot cleaner.

The only thing you have to be careful about is making sure not to write

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

or else you'll double-retain the object and end up with a memory leak.

Properties make it your code look like assignment, but in reality they're the same as traditional accessor methods you might have written yourself prior to Obj-C 2.0. With properties Obj-C is simply generating the accessor methods behind the scenes for you instead using the keywords you specify in the declaration (assuming you use @synthesize and don't write your own accessor methods anyway).

No, there is no memory leak. The code in your second example is logically equivalent to

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

because the @synthesized setter is logicall equivalent to

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

It's worth noting that setting foo directly is probably not something you want to do outside of an init method. If you assign a value to foo directly, you bypass the automatic KVO notification (you would have to wrap your assignment in a willChangeValueForKey:/didChangeValueForKey: pair) and you break any subclass' behavior if it overrides the setFoo: method, expecting all modifications of foo to go through the setter.

You assign directly to foo in an init method because the setFoo: method or a subclass' overriden setFoo: method may have side-effects or depend on the instance's fully initialized.

Similarly, you would use [foo release] rather than self.foo = nil; in the -dealloc method for the same reasons.

All the answers so far assume that “ foo ” in the first line of the second example is the instance variable behind the foo property. This is the default behavior.

If the foo that the first line assigns to is a local variable, then the foo property is irrelevant, and you will leak the object unless you release it later in the method.

If foo is an instance variable, but the foo property is actually backed by a different instance variable, or no instance variable at all, then (a) you are writing hard-to-maintain code and (b) it may be a leak.

Finally, echoing the previous answers: If foo is the instance variable backing the foo property, then this is not a leak, since the setFoo: method that you call in the second line will release the object that you put in the foo instance variable in the first line.

Since nobody else seems to have noticed: There might be a leak.

I'll assume that foo is both an ivar and a retain property:

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

Let's say your code looks something like this:

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

That, in itself does not leak provided foo was nil to start with . That doesn't mean it won't leak — let's say you add some more code:

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

-(void)fubar {
  [self baz];
  [self bar];
}

Stuff like foo = [[Foo alloc] init] is generally safe in init -methods because it's assumed that you only call one of them, so foo is guaranteed to be nil initially. Everywhere else, you have to be a bit more careful.

// 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;

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM