简体   繁体   中英

Method returns an Objective-C object with a +1 retain count

Since upgrading to Lion and consequently to XCode 4.1

I get dozens of "potential memory leak" when running the Analyzer.

I would typically use a property list like so:

@synthesize indexPath = _indexPath;

- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle {
    self = [super initWithNibName:nibName bundle:nibBundle];
    self.indexPath = [[NSIndexPath alloc] init];
    [_indexPath release];
    return self;
}

and in dealloc() method:

- (void)dealloc {
    [_indexPath release];
    [super dealloc];
}

Now, analyze will show me the dreaded blue message on self.indexPath telling me that there's a leak. When there's obviously none.

How do you allocate and format your code so XCode doesn't believe it's leaking? (while keeping the property alias self.var vs _var)

Thank you...

The other answers already explain the problem in depth, anyway these are some common patterns that you can use to avoid this error:

NSIndexPath *ip = [[NSIndexPath alloc] init];
self.indexPath = ip;
/* ... */
[ip release];

indexPath = [[NSIndexPath alloc] init];

self.indexPath = [[[NSIndexPath alloc] init] autorelease];

self.indexPath = [NSIndexPath indexPathWithIndex:...];

In init you are really supposed to use the ivars for setting directly:

- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle {
    self = [super initWithNibName:nibName bundle:nibBundle];
    _indexPath = [[NSIndexPath alloc] init];
    return self;
}

Perhaps that would cure the issue? It would follow convention.

The issue is that by using the setter method, the compiler cannot guarantee that the same object you passed to the setter will actually be assigned to the backing ivar. After all, your setter method may do all sorts of strange stuff, least of which could include making a copy of the passed object.

In other words, the compiler cannot guarantee that self.indexPath == _indexPath , so your call to release may be on a different object than the one you got from the init method. Thus, it gives you a warning that your memory management may be incorrect, which is appropriate.

Thus, you need to guarantee that you're calling release on the same object that you passed to the setter. In short:

NSIndexPath *tmpPath = [[NSIndexPath alloc] init];
self.indexPath = tmpPath;

[tmpPath release];           // This is the only correct way to do it.
// [self.indexPath release]; // WRONG! May not be the same object as tmpPath
// [_indexPath release];     // WRONG! May not be the same object as tmpPath

As others have mentioned, though, in an init method it's generally better to just assign to the ivar directly. Thus:

_indexPath = [[NSIndexPath alloc] init];

Try this:

NSIndexPath *tmpPath = [[NSIndexPath alloc] init];
self.indexPath = tmpPath;
[tmpPath release];

The static analyzer is probably just looking at that one line as it is not clever enough to realise that you were actually attempting to rectify the issue.

I would use this pattern

NSIndexPath *tmpPath = [[NSIndexPath alloc] init];
self.indexPath = tmpPath;
[tmpPath release];

More in depth explanation. So when the analyzer looks at the line

self.indexPath = [[NSIndexPath alloc] init];

It see's the +1 retain from this part

[[NSIndexPath alloc] init]

and it see that the self.indexPath is compiled to

[self setIndexPath:[[NSIndexPath alloc] init]];

This method (if auto generated by @synthesize ) will probably look like this

- (void)setIndexPathL(NSIndexPath *)indexPath
{
    if (_indexPath != indexPath) {
        [_indexPath release];
        _indexPath = [indexPath retain];
    }
}

So now the analyzer see's that there is another retain on indexPath.

So that's 2 x +1 retain and it can only assume that you will release once in the dealloc.

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