NSMutableArray in loop leaks memory even when I explicitly release it - objective-c

This loop leaks memory:
int64_t i,verylongnumber;
//misc. code
for(i=0;i<verylongnumber;i++){
NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
nil];
[myArray removeAllObjects];
[myArray release];
}
I've tried everything to keep it from leaking memory, but I can't. I think it has something to do with the NSNumbers. I assume they are created autoreleased, but does that mean I have to free them individually (i.e. use alloc)? How would I even do that? Create a separate variable for each NSNumber and insert that into the array? That seems like a lot of work. I tried [myArray removeAllObjects], but that made no difference. it is within my own thread with its own autorelease pool. I'm not sure if that makes a difference.
This fixed it:
I added an additional autorelease pool inside the loop:
int64_t i,verylongnumber;
//misc. code
for(i=0;i<verylongnumber;i++){
NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init];
NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
[NSNumber numberWithLongLong:65535],
nil];
[myArray release];
[pool2 drain];
}

I'll take a stab at this..
You can remove [myArray removeAllObjects] as it is redundant. NSArray's do retain their objects, but they also release them when the array itself is deallocated.
The NSNumbers themselves are autoreleased. However if you do a very very large loop then that autoreleased memory won't actually be freed until the for loop exits and eventually the run loop as well (unless you have setup a separate NSAutoreleasePool somewhere).
So I can see how the memory usage would increase as this loop iterates, but at completion it should free the memory. How did you arrive at the conclusion that you have a leak?

Are you waiting to see if the objects get released in the near future?
Autoreleased objects are released at some point in the near future. In the case above, you're looping a very long number of times creating many objects. They will not get released within the scope of that code.
In a GUI app it means after the function returns when the run loop is being run. In a console app, it's when the pool is released.
Check out:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html
Autorelease pools provide a mechanism whereby you can send an object a
“deferred” release message.
The key is the deferred point.
EDIT: (after comment)
Note that you can drain the pool. The other option is to create non-autoreleased numbers (alloc/init) and explicitly release in your long running loop. depending on the code, that may be desirable since draining the pool could release objects deferred for release which code later in that loop/scope assumes is still deferred. If you want to control it, then control it.

NSArray retains objects when they're added to NSArray. This means that NSArray takes ownership of that object. when the object is removed from NSArray or NSArray destroyed, the object is released (reference count -1). If object dont have any other owner then object is destroyed.
The following code will create memory leak
NSNumber *number = [[NSNumber alloc]initWithFloat:floatValue]; //reference count is 1, you are the owner
[aArray addObject:number] //reference count is 2, aArray is also owner.
So to remove memory leak , you shoul release number.
NSNumber *number = [[NSNumber alloc]initWithFloat:floatValue]; //reference count is 1, you are the owner
[aArray addObject:number] //reference count is 2, aArray is also owner.
[number release]; // reference count is 1, you are not owner og number
If you are adding autorelease object to NSArray, no need to release that object. when the autorelease pool is popped that object will loose its ownership.
In your example with each for loop, NSNumber is created while the old one is still hanging around in memory waiting for the autorelease pool to be released.

Related

Why is memory sometimes deallocated immediately and other times only when autorelease pool is drained?

I made simple experiment and found some strange behavior. Here some code - part of long method with ARC enabled:
MKObject *obj = [[MKObject alloc]init];
NSMutableArray *temp = [[NSMutableArray alloc]init];
[temp addObject:obj];
obj = nil;
temp = nil;
//here deallocating is called on obj (MKObject)
//other stuff
but if I change NSMutableArray to NSArray and literal initialisation
NSArray *temp = #[obj];
deallocating executed before autoreleasepool closed, not after setting nil to all references. Did I missed something ?
A few observations:
In your first example, neither the MKObject nor the NSMutableArray is an autorelease object, so these objects will be deallocated immediately, not waiting for the autorelease pool to drain:
MKObject *obj = [[MKObject alloc] init];
NSMutableArray *temp = [[NSMutableArray alloc] init];
[temp addObject:obj];
obj = nil;
temp = nil;
In your second example, the NSArray is an autorelease object, so the NSArray (and therefore, the MKObject) will not be deallocated until the autorelease pool is drained.
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = #[obj];
obj = nil;
temp = nil;
To understand why the array literal, #[], creates an autorelease object, one should note that it expands to +[NSArray arrayWithObjects:count:]. Autorelease objects are created whenever you instantiate an object with any method other than using alloc followed by an init (whether init, or one of the permutations, such as initWithObjects:).
As you observed, when an app creates autorelease object, the object will not be immediately be deallocated, but it will when the autorelease pool drains. Since we generally yield back to the runloop quickly (at which point the pool will be drained), the choice of autorelease objects or non-autorelease objects has little practical impact in simple cases. But if the app, for example, has a for loop in which it creates many autorelease objects without yielding back to the runloop, it could be problematic (especially if MKObject was large or you were doing this many times). For example:
for (NSInteger i = 0; i < 100; i++) {
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = #[obj];
// Note, because they are local variables which are falling out of scope, I don't have to manually `nil` them.
}
Because we are instantiating autorelease NSArray objects in this example, the above would keep all 100 arrays and objects in memory until you yielded back to the runloop and the autorelease pool had a chance to drain. This means that the app's "high water mark" (the maximum amount memory it uses at any given time), would be higher than it might need to be. You could remedy this by either:
use a non-autorelease object (such as by using alloc/init) instead of using the array literal:
for (NSInteger i = 0; i < 100; i++) {
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = [[NSArray alloc] initWithObjects:obj, nil];
}
or
by introducing your own, explicitly declared #autoreleasepool:
for (NSInteger i = 0; i < 100; i++) {
#autoreleasepool {
MKObject *obj = [[MKObject alloc] init];
NSArray *temp = #[obj];
}
}
In this final example, the autorelease pool will be drained for each iteration of the for loop, resolving any challenges with autorelease objects that would otherwise have their deallocation deferred until the end of the loop.
One final caveat: In general, methods that begin with alloc and init (or a variation of the init method), will not generate autorelease objects, whereas all other methods, such as arrayWithObjects:count: will generate autorelease objects. One notable exception is the NSString class, which due to internal memory optimizations, does not conform to this rule. So, if you have any doubt, you can employ your own manual #autoreleasepool if you are repeatedly instantiating and releasing objects, and you are unsure as to whether the objects are autorelease objects or not. And, as always, profiling your app with the Allocations tool in Instruments is a good way of observing the app's high water mark. For an illustration of how these various techniques can impact the memory usage of your app, see https://stackoverflow.com/a/19842107/1271826.
The array was being retained by the autorelease pool. As described in Clang's Objective-C Literals documentation, the array literal syntax expands to a call to +[NSArray arrayWithObjects:count:], which creates an autoreleased array.
A couple of things I see, though I'm not entirely clear on the question so I can't say which applies:
Adding an object to an NSArray or NSMutableArray increments the object's retain count.
In the first instance, you manually instantiate obj, which gives it retain count 1.
Adding it to the NSMutableArray makes its retain count 2.
In this case, obj = nil decrements retain to 1;
temp = nil tells the array to handle releasing its contents. Those w/retain count 0 get dealloc'd immediately.
In the 2nd instance with #[] literal creation, the literal syntax under the hood creates an autoreleased object using the method arrayWithObjects: count:. When this is no longer needed it goes to the autorelease pool for eventual deallocation.
It isn't an issue of the objects IN the array but the way the arrays themselves were created.
Edited my original response to address comments below - I was confusing the issue.

Init an object, then store it into an NSArray. Is this going to be a leak?

If an inited object comes to me retained, so I own it, and I store it in an NSArray, which retains that which gets stored in it, can I count on NSArray to see that it's already retained and not increase the count, or do I need to run through the array and decrement the retain count to insure no memory leak?
Sounds like you need to read the Memory Management Programming Guide. Your case is extremely simple. You own the object. You pass it to the array, which now also owns it. You need to release your ownership of it. Otherwise you'll leak it.
To make sure that the ownership of the object which was added into the NSArray is relinquished, send the -release message to the object right after you add it to the NSArray. If you do not do this, then you will indeed have a memory leak.
This is what happens:
NSString *str = [[NSString alloc] initWithFormat:#"%#", #"Blah"]; //retain count is 1, you own this object
[array addObject:str]; //retain count gets bumped to 2
[str release]; //retain count is 1 - relinquishing ownership here.
//There is no leak because when the NSArray is
//deallocated, the object will be sent the release message.
But if you don't send the owned inserted object the -release message, then even when the NSArray is deallocated, the object will only have a retain count of 1 and the memory obtained by the object will never be reclaimed, thereby resulting in a leak.
Whenever you release the NSArray, it'll release everything it retains.
As such, as long as you release the inited object once you've added it to the NSArray (so it's the only thing that retains it) or release it once you've finished with it outside of the array all should be fine.
Incidentally, there's a good blog post called "objective-c memory management for lazy people" that explains such things pretty well and is a handy reference if you're just starting out with such things.
You don't need to do that. NSArray takes ownership of any object that it stores. It will release its objects when it's deallocated. If you retain an object yourself, you take ownership too, and you are responsible for releasing it too.
NSArray will retain your object when you add it, and then release it when you remove it from the array. This is by design. This means that to ensure there's no memory leak, if you already retained the object before adding it to the array, you should release it after removing it from the array:
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:10];
NSObject *object = [[NSObject alloc] init]; // retain count of 1 (because of alloc)
[object retain]; // useless, just for example, retain count of 2 (because of retain)
[array addObject:object]; // array is mutable, retain count of 3 (because of addObject:)
[array removeObject:object]; // retain count of 2
[object release]; // retain count of 1
[object release]; // retain count of 0, the object is dealloc'd afterwards
[array release]; // to be sure that we are not leaking an array, too

How to release an object from an Array?

I am currently working on an demo app so I was a little sloppy how to get things done, however I run the "Build and Analyze" to see how many leaks I get,... well and there are a lot.
Source of teh proble is that I have a NSMutableArray and I add some Objects to it :
NSMutableArray *arr = [[NSMutableArray alloc] init];
[arr addObject:[[MyObject alloc] initWithText:#"Option1"]];
// I have like 100 lines like that and 100 complains
Now, xcode complains about a potential leak.
Can someone give me some advice how to handle that ?
Thanks.
The problem is that you're allocating an instance of MyObject which you have a responsibility to release. When you pass it to the array, the array also retains the object, so now both you and the array have to release it. You can simply autorelease the object, and the array will keep it retained until you remove the object from the array or destroy the array itself.
[arr addObject:[[[MyObject alloc] initWithText:#"Option1"]] autorelease];
Replace
[arr addObject:[[MyObject alloc] initWithText:#"Option1"]];
with
[arr addObject:[[[MyObject alloc] initWithText:#"Option1"] autorelease]];
Most collections (arrays, dictionaries) own the objects added to them. And, since you’ve sent +alloc to MyObject, you also own the object that’s just been instantiated. As the memory management rules say, you are responsible for relinquishing ownership of objects you own. Sending -autorelease to the newly instantiated object will do that.

Returning an NSArray without Leaking?

I have been struggling with the best pattern for returning an array from a static method.
In my static method getList (in the BIUtility Class), I am allocating an NSArray to return. in the return line, I do:
return [array autorelease];
Then in the calling method, I am allocating an array like this:
NSArray * list = [[[NSArray alloc] initWithArray:[BIUtility getList]] retain];
Later I release the list using:
[list release];
I think this is causing a memory leak as the retain is increasing the retain count one too many. However, if I do not do the retain, I get a Bad_Exec because it has already freed the class.
I feel like I am overthinking this and there must be a typical pattern. I have been looking all over the place and I cannot find a "best practice".
I appreciate your help.
You should replace:
NSArray * list = [[[NSArray alloc] initWithArray:[BIUtility getList]] retain];
With:
NSArray * list = [[BIUtility getList] retain];
This is because getList actually returns a pointer to the NSArray.
If it were a mutable array, however, you should say [[BIUtility getList] copy]; so that you don't accidentally mutate an array that another object has a reference to.
If you are curious, you were getting a memory leak because your original statement increments two counters, while you only release one later.
These parts of the statement increase counts:
[anObject]] retain]
[anClassname alloc]
[anObject copy] will also create an object with a count of 1.

Does [NSMutableDictionary setValue: value forKey: key] retain NSString key?

When adding items to NSMutableDictionary using the setValue:forKey: method (I suppose this generalizes to any NSObject) does the dictionary retain the second parameter, the NSString?
For example:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSString *theString = #"hello";
int i;
for (i=0; i<[theString length]; i++){
NSNumber *myInt = [NSNumber numberWithInt:i];
NSString *character = [NSString stringWithFormat:#"%C",[theString characterAtIndex:i]];
[dict setValue: myInt forKey:character];
}
[dict release];
[pool release];
Clearly, there is no reason to release myInt in the loop, it is retained by dict so it can't be released until the end of the code. But is the same true of character? My thinking is that if NSMutableDictionary stores the string in some other way, then one could create a temporary pool around the loop and release those strings instead of waiting until the release of the dictionary.
I am also curious as to why retainCount of character is 7fffffff as if it is an NSConstantString, I would expect stringWithFormat to return an NSString object which would need retaining, but that doesn't seem to be the case.
It's very common in Cocoa for NSString parameters to be copied instead of retained. That's because you could have just as easily given the dictionary an instance of NSMutableString. Because the string's value could change, NSDictionary makes a copy.
But, regardless of how NSMutableDictionary really operates, you don't have to worry whether character needs to be retained. Once you've passed it to NSMutableDictionary as a parameter, it's really that class's problem to decide how to store the data, unless the documentation specifically tells you that retaining the objects are your responsibility.
I also wouldn't worry too much about the retainCount of any object. Following the retain count of an object too closely can lead you down rabbit holes that just make you spin your wheels.
Finally, I really don't think you need to create your own autorelease pool here. Unless you know with absolute certainty that theString is going to be very long, or you've already observed high memory utilization in Instruments, adding the autorelease pool is an unnecessary optimization.
You don't need to retain character there, the dictionary retains it when you set it as a key and your own code has no need to retain it.
You also don't need to worry about why the retain count isn't what you expect. Maybe the Foundation framework has Flyweight-like instances of a load of single-character NSString instances. In any case if you've got the memory management correct following the guidelines, you'll be OK regardless of what the framework's doing behind the scenes. http://iamleeg.blogspot.com/2008/12/cocoa-memory-management.html