views:

5922

answers:

2

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?

+2  A: 

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.

Louis Gerbarg
**All** of those methods return an object with a retain count of 1. The difference is just that with some you own the object and thus are required to release it, and with the others you don't own the object and aren't required to release it (but also can't count on it being around past the current call chain).
Chuck
Strictly speaking, no they don't, its an implementation detail. In a number of cases they return things with different retain counts. For instance =[UIImage imageNamed:] may return something with a very large large retainCount because it could reuse a cached image.
Louis Gerbarg
Well, yes, the retain count itself is an implementation detail. Apple's docs say as much. And in all the cases you listed above, on current versions of OS X, the value of this implementation detail is 1.
Chuck
Sure. Strictly speaking no one should ever actually think about absolutely retain counts so much as relative retain counts (which include any pending autoreleases). In those terms everything either returns +1 or 0 depending on implied ownership.
Louis Gerbarg
+16  A: 

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.

John Calsbeek