views:

194

answers:

3

In the following code:

- (NSMutableArray *) fetchNotesForGroup: (NSString *)groupName {

 // Variables declaration
 NSMutableArray *result;
 NSFetchRequest *fetchRequest;
 NSEntityDescription *entity;
 NSSortDescriptor *sortDescriptor;
 NSPredicate *searchPredicate;
 NSError *error = nil;

 // Creates the fetchRequest and executes it
 fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
entity = [NSEntityDescription entityForName:@"Note" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"noteName" ascending:YES] autorelease];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[fetchRequest setReturnsDistinctResults:YES];
searchPredicate = [NSPredicate predicateWithFormat:@"categoryName like %@", groupName];
[fetchRequest setPredicate:searchPredicate];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:@"noteName"]];
result = [[managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];

 // Variables release

 return result;
}

... I Fetch notes for a given categoryName. When I'm running Instruments, it says that a NSCFString is leaking.

I know leaks are mean for iPhone developers... but I don't have any idea on how to plug this one.

Any clues? All help is welcome.

Thanks a lot!

A: 

First of all, Instruments may not be always accurate.
It can report leaks on some special case, just because it don't see you are actually releasing the object elsewhere.

It's also possible that some parts of the CF have leaks.

What I can see in your code is that you are using auto-released objects.
You're temporary objects will have a long life-cycle.
What append if you release them explicitly instead, just before the method's return?

You are also returning a copy of an object.
You have to make sure that object is released at some point.
It may be much better to return an auto-released object, and let the calling method decide if the object should be retained.

Macmade
A: 

Since you don't create any strings, the leaking string is most likely one of the strings that ends up inside the fetch request and it is most likely the fetch request that is actually leaking. (The call stack shown in Instruments should confirm this.)

I don't think you have an actual leak but by doing this:

 fetchRequest = [[[NSFetchRequest alloc] init] autorelease];

... you are allowing the fetchRequest to live beyond the scope where it was defined Instruments will interpret that as a leak.

Autorelease actually makes objects live longer than direct release because it causes objects to hang around until the outer autorelease pool is drained. For example, if you create object-B inside object-A and return it to object-C autoreleased, object-B will stay alive long after object-A has been deallocated even if object-C never retains it. (Although it will eventually die at an unpredictable moment.)

Autorelease is not a convenience method for retaining. It has a specific purpose of retaining objects that are being passed between other objects. If your not doing this, don't use autorelease.

If you do this:

fetchRequest = [[NSFetchRequest alloc] init];
// ...
[fetchRequest release];

... your leak will go away.

You might, however, want to do this:

return [result autorelease];

... to ensure that the result array lives long enough to be retained by another object.

TechZen
Firstly: autorelease can absolutely be used as a convenience method for releasing an object. It's a tradeoff - readability/convenience vs possible short-term RAM bloat. I tend to use autoreleased objects whenever possible, for readability, except if I'm in a loop or some other "hot" section of code, or when the object uses a lot of RAM.Secondly: Instruments doesn't show properly auto-released objects as leaks - go and try it for yourself.
Nick Forge
@Nick Forge -- that's a common mistake. The Apple docs make it very clear it is not a convenience method. The flipside you seemed to have missed is that an autorelased object can die at anytime in any scope. You've gotten away with it because you've been writing simple code. If you had tightly managed memory and custom release pools you would see how dangerous such a sloppy practice is.
TechZen
It's not "sloppy", or a "mistake", it's a trade-off, and different people have different philosophies on it. You prefer to prioritise RAM optimisation over readability, and that's fine. You're free to do that. Any bugs resulting in using autorelease instead of release, however, are due to other problems in your code (e.g. not setting a delegate to nil or something similar). See this question (and the many insightful answers) for a wide range of opinions on the topic: http://stackoverflow.com/questions/193288/what-is-the-cost-of-using-autorelease-in-cocoa
Nick Forge
+1  A: 

Your problem is this:

result = [[managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
// Variables release
return result;

mutableCopy returns an owning reference (ie, an object with a +1 retain count), which you are responsible for (auto)releasing. You don't, and you then relinquish the reference, which means you've leaked the array.

Use return [result autorelease]; instead.

Dave DeLong
Thanks for the answers. I tried to release the fetch myself (not with autorelease) and it didn't solve.I do release the array in another function, after using it, and the leak remains...Guess I'll have to try something else... not sure what.
camilo
Heh, I didn't even see the mutable copy. Guess that'll teach me to scroll all the way over. In any case, you shouldn't be copying here but rather in what ever object you send the array to.
TechZen
I don't think that Instruments tracks leaks in an absolute sense but rather looks for objects that persist past their expected scope. If you don't follow standard methods of managing retention, then you might find Instruments confused by objects that hang around after they should have disappeared with the scope that created them.
TechZen
@camillo: You shouldn't release the object in another method - your `-fetchNotesForGroup:` method should return an autoreleased object. You're explicitly breaking Cocoa memory management idioms here, which may be why Instruments is showing a leak. Try reading the "Memory Management Rules" page in the "Memory Management Programming Guide for Cocoa": http://developer.apple.com/iphone/library/documentation/cocoa/conceptual/MemoryMgmt/MemoryMgmt.html
Nick Forge
A comment just to say "thanks". I didn't accept any answer yet because I wasn't able to try them (I have been dealing with more urgent problems). Anyway, it makes perfect sense. I shall return (hopefully) with an "Accepted" flag.
camilo