views:

100

answers:

3

I have 2 arrays, 1 in the viewDidLoad method and 1 in the add method(adds an object to favorites)

NSUserDefaults *myDefault = [NSUserDefaults standardUserDefaults];
    NSArray *prefs = [myDefault arrayForKey:@"addedPrefs"]; 
    userAdded = [[NSMutableArray alloc] initWithArray:prefs];

Instruments is showing leaks from these prefs NSArrays. (only one shown above, other is exactly the same in ViewDidLoad) When I try to release them the app crashes and they are defined locally, so I cannot release them in the dealloc method.

Is it possible to assign my userAdded NSMutable array directly to the arrayForKey? Or will it cause a mismatch?

How can this leak be stopped?

A: 

The leak is that you alloc without a release. Every alloc (or copy) should be followed by a release or autorelease somewhere (but not too soon - which may have led to your crash?)

If you need to hold on to the userAdded beyond the scope of your function, make it a retained property and assign to self.userAdded.

So make it

self.userAdded = [[[NSMutableArray alloc] initWithArray:prefs] autorelease];

and have a

@property (nonatomic,retain) NSMutableArray * userAdded;

and a

@synthesize userAdded;

to plug the leak.

mvds
Thanks. I do that except for the autorelease, but Instruments says it is an NSArray which is leaking. The NSArray is the prefs array from NSUserdefaults. If I release this prefs at the end of the method the app crashes?
alJaree
That's correct - if you release prefs, you will crash. prefs comes from a "factory" method (i.e. not one that has alloc/init), so it comes pre-autoreleased and will be released when that block goes out of scope. So, if it crashes there - good! That's what you want!However, if "prefs" is leaking, it could have had a retain count from somewhere else in your code. Can you use Instruments to show the full history of the object allocation?
phooze
The `NSArray` might be retained by the `NSMutableArray` (see my comment to the other question) so it would still leak if you don't release the `NSMutableArray` properly.
mvds
A: 

The documentation doesn't specify if initWithArray: sends the objects in the old array (prefs) an additional retain when added to the new array (userAdded). It should, because the items are being added to the new array. Maybe when the old array (prefs) is deallocated, the retain count on the elements in userAdded drops too low, the objects are deallocated also, and the app crashes. To check this, I asked the contents of the first array for their retain count before and after calling initWithArray:.

NSUserDefaults *myDefault = [NSUserDefaults standardUserDefaults];
NSArray *prefs = [myDefault arrayForKey:@"addedPrefs"]; 
NSUInteger i, count = [prefs count];
for (i = 0; i < count; i++) {
    NSObject * obj = [prefs objectAtIndex:i];
    NSLog(@"Object: %@, Retain count: %d.", obj, [obj retainCount]);
}
userAdded = [[NSMutableArray alloc] initWithArray:prefs];
for (i = 0; i < count; i++) {
    NSObject * obj = [prefs objectAtIndex:i];
    NSLog(@"Object: %@, Retain count: %d.", obj, [obj retainCount]);
}

The objects report an increased retain count, so the prefs array can be released without affecting the new array. But running this code still leaks an NSArray.

So the problem must lie with the prefs array. Since the NSUserDelfaults method arrayForKey: produces the prefs array, yet the words "alloc", "new", or "copy" are not found in this method's name, the calling method does not own the prefs array. Most likely, the prefs array was added to an autorelease pool. To test this idea, I surrounded the above test code with calls to NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; and [pool drain]. Running this code does not leak an array.

So I think the prefs array is being added to an autorelease pool that doesn't get drained. The simplest way to eliminate this leak would be to make an autorelease pool before creating the prefs array, and draining that pool when done with the prefs array.

Mr. Berna
we don't know the inner workings of `initWithArray:`, it might as well `retain` the `NSArray` it's given, so there's a danger in trying to understand retain counts. (I would think: why wouldn't it, as `NSArray` is immutable? It could keep track of changes to `NSArray`, or only make it's own copy on write or something like that.)
mvds
"The documentation doesn't specify if initWithArray: sends the objects in the old array (prefs) an additional retain when added to the new array" yes it does. Arrays always retain their elements - or do something to stop them from being deallocated..
JeremyP
Now I am confused.Should I use the last method described in the above question? userAdded = [[NSMutableArray alloc] etc. copyItems:YES];
alJaree
All of my leaks are coming from the various places I initWithArray someNSUserdefaultsArray. I would have thought that initWithArray would fill the array with the objects, but I guess not if there is a copyItems call. So I should just copyItems:YES and then release the NSUserdefaults array?
alJaree
The snippet above still leaks an `NSMutableArray` which is alloced but never released. See my answer below.
mvds
@mvds The original question said that the NSArray is leaking, not the NSMutableArray. The point of this code is to convert an array into a mutable array. Of course the NSMutableArray will hang around longer, so it can be used. Since the reference variable, userAdded, is not declared in this snippet, I guessed that it is an instance variable, and released in that object's dealloc method.
Mr. Berna
@berna Agreed, but given the frequency with which we see iPhone memory issues coming by here (some 20 since I joined last week), I try not to miss any opportunity to elaborate on it.
mvds
A: 

Release userAdded in -dealloc.

tc.