I'm new to memory managed code but I get the idea pretty well.
On taking my app through the leaks tool in XCode, I noticed I only had to clean up my custom objects, but not dynamically created arrays for example, so I figured those data types are autoreleased - makes sense since I only had to release the arrays I used as properties that had a (retain) on them.
Then I noticed something peculiar : I was getting a leak on a certain array initialized like this :
NSMutableArray *removals = [NSMutableArray new];
but not a similar one
NSMutableArray *removals = [NSMutableArray arrayWithCapacity:9];
Now, the reason one was set up with "new" is that it could have 0-99 items in it, whereas the other one I knew was going to always be 9. Since both arrays are passed to the same method later based on user interaction, I was either getting a leak if I did not release at the end of the method, or an exception if I did!
I changed the first array to
NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99];
and I get no leaks and don't have to release anything. Can anyone explain?
As noted in the memory management rules, whenever you have an object that you have created with +alloc
, +new
, -copy
, or -mutableCopy
, you own it and are responsible for releasing it at some point. (In fact, +new
is just shorthand for [[MyClass alloc] init]
.) As you noted, creating an array via [NSArray new]
without releasing it is a memory leak. However, if you handle this object properly, it is usually possible to release it at some point. For example:
If the method that uses the array is called from within the method that creates the array, then you should be able to release the array after it has been used. If the inner method needs to keep a more permanent reference to the array around, then that method is responsible for sending -retain
and, eventually, -release
to the object. For example:
- (void)myMethod {
NSArray *removals = [NSArray new];
// ...
[someObject someOtherMethod:removals];
[removals release];
}
If you created the array in an -init
method for an object, then the -dealloc
method can release it when the object is destroyed.
If you need to create the array and then return it from the method, you've discovered the reason that autoreleasing was invented. The caller of your method isn't responsible for releasing the object, since it isn't an +alloc
, +new
, -copy
, or -mutableCopy
method, but you need to ensure it is released eventually. In this case, you manually call -autorelease
on the object before you return it. For example:
- (NSArray *)myMethod {
NSArray *removals = [NSArray new];
// ...
return [removals autorelease];
}
When you create the array via +arrayWithCapacity:
, you aren't calling one of the "special" methods, so you do not have to release the result. This is probably implemented with -autorelease
, much like the last example above, but not necessarily. (Incidentally, you can also create an empty autoreleased NSMutableArray with [NSMutableArray array]
; the method is found in NSArray, so it won't show up in the documentation under NSMutableArray, but it will create a mutable array when sent to the NSMutableArray class.) If you're going to be returning the array from your method, you can use this as shorthand for [[[NSMutableArray alloc] init] autorelease]
—but it is just a shortcut. In many situations, though, you can create an object with -init
or +new
and manually release it at the appropriate time.
This is how things implemented behind the scene:
+(NSMutableArray*) new
{
return [[NSMutableArray alloc] init];
}
and
+(NSMutableArray*) arrayWithCapacity:(NSNumber)capacity
{
return [[NSMutableArray alloc] initWithCapacity:capacity] **autorelease**];
}
In first case the array is allocated only and you're responsible for de-allocating it. In contrary the arrayWithCapacity has autoreleased for you and won't cause leak even you forget to deallocate.
Cocoa uses certain naming conventions. Anything that starts with alloc, new, or copy returns something with a retainCount of 1 and you are required to release. Anything else that a function returns has a balanced retainCount (it might be held by something else, or it might be retained and out released).
So:
NSMutableArray *removals = [NSMutableArray new];
Has a retainCount of 1, and:
NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99];
or
NSMutableArray *removals = [NSMutableArray array];
Don't since the methods are not prefixed with alloc, new or copy. This is all spelled out in the memory management documentation. In particular:
You take ownership of an object if you
create it using a method whose name
begins with “alloc” or “new” or
contains “copy” (for example, alloc,
newObject, or mutableCopy), or if you
send it a retain message. You are
responsible for relinquishing
ownership of objects you own using
release or autorelease. Any other time
you receive an object, you must not
release it.