Use autorelease before adding objects to a collection?
Asked Answered
T

7

5

I have been looking through the questions asked on StackOverflow, but there are so many about memory management in Objective-C that I couldn't find the answer I was looking for.

The question is if it is ok (and recommnded) to call autorelease before adding a newly created object to a collection (like NSMutableArray)? Or should I release it explicitly after adding it. (I know NSMutableArray willl retain the object)

This illustrates my question:

Scenario A (autorelease):

- (void) add {
   // array is an instance of NSMutableArray

   MyClass *obj = [[[MyClass alloc] init] autorelease];

   [array addObject:obj];
}

Scenario B (explicit release):

- (void) add {
   // array is an instance of NSMutableArray

   MyClass *obj = [[MyClass alloc] init];

   [array addObject:obj];

   [obj release];
}

I assume both are correct, but I am not sure, and I sure don't know what the preffered way is.

Can the Objective-C gurus shed some light on this?

Tbar answered 18/7, 2009 at 15:20 Comment(0)
B
7

Both are correct and will work as you're expecting them to.

I personally prefer to use the latter method, but only because I like to be explicit about when objects get released. By autoreleasing the object, all we're doing is saying "this object will get released at some arbitrary point in the future." That means you can put the autoreleased object into the array, destroy the array, and the object might (probably) still exist.

With the latter method, the object would get destroyed immediately with the array (providing that nothing else has come along and retained it in the meantime). If I'm in a memory-constrained environment (say, the iPhone) where I need to be careful about how much memory I'm using, I'll use the latter method just so I don't have so many objects lingering in an NSAutoreleasePool somewhere. If memory usage isn't a big concern for you (and it usually isn't for me, either), then either method is totally acceptable.

Barnhill answered 18/7, 2009 at 15:30 Comment(1)
If you want your code to be absolutely safe it is strongly recommended to used autorelease: See #1148285Bouffard
M
12

IMHO, which way is 'right' is a matter of preference. I don't disagree with the responders who advocate not using autorelease, but my preference is to use autorelease unless there is an overwhelmingly compelling reason not to. I'll list my reasons and you can decide whether or not their appropriate to your style of programming.

As Chuck pointed out, there is a semi-urban legend that there's some kind of overhead to using autorelease pools. This could not be further from the truth, and this comes from countless hours spent using Shark.app to squeeze the last bit of performance out of code. Trying to optimize for this is deep in to "premature optimization" territory. If, and only if, Shark.app gives you hard data that this might be a problem should you even consider looking in to it.

As others pointed out, an autoreleased object is "released at some later point". This means they linger around, taking up memory, until that "later point" rolls around. For "most" cases, this is at the bottom of an event processing pass before the run loop sleeps until the next event (timer, user clicking something, etc).

Occasionally, though, you will need to get rid of those temporary objects sooner, rather than later. For example, you need to process a huge, multi-megabyte file, or tens of thousands of rows from a database. When this happens, you'll need to place a NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; at a well chosen point, followed by a [pool release]; at the bottom. This almost always happens in some kind of "loop batch processing", so it's usually at the start and bottom of some critical loop. Again, this should be evidence based, not hunch based. Instrument.app's ObjectAlloc is what you use to find these trouble spots.

The main reason why I prefer autorelease to release, though, is that it is much easier to write leak-free programs. In short, if you choose to go the release route, you need to guarantee that release is eventually sent to obj, under all circumstances. While this seems like it might be simple, it is actually surprisingly hard to do in practice. Take your example, for instance:

   // array is an instance of NSMutableArray
   MyClass *obj = [[MyClass alloc] init];
   [array addObject:obj];
   // Assume a few more lines of work....
   [obj release];

Now imagine that for some reason, something, somewhere, subtly violates your assumption that array is mutable, maybe as the result of using some method to process the results, and the returned array containing the processed results was created as a NSArray. When you send addObject: to that immutable NSArray, an exception will be thrown, and you will never send obj its release message. Or maybe something goes wrong somewhere between when obj was allocd and the required call to release, like you check some condition and return() immediately by mistake because it slipped your mind that that call to release later on must take place.

You have just leaked an object. And probably signed yourself up to several days of trying to find out where and why it is your leaking it. From experience, you will spend many hours looking at that code above, convinced that it could not possibly be the source of the leak because you very clearly send obj a release. Then, after several days, you will experience what can only be described as a religious epiphany as you are enlightened to the cause of the problem.

Consider the autorelease case:

   // array is an instance of NSMutableArray
   MyClass *obj = [[[MyClass alloc] init] autorelease];
   [array addObject:obj];
   // Assume a few more lines of work....

Now, it no longer matters what happens because it's virtually impossible to leak obj accidentally, even under extremely unusual or exceptional corner cases.

Medlin answered 19/7, 2009 at 1:22 Comment(2)
This came up in my new question here: stackoverflow.com/questions/2911678. Great point.Moreira
[pool drain] would seem like the better choice in the case that iOS ever gets GCOrchestral
B
7

Both are correct and will work as you're expecting them to.

I personally prefer to use the latter method, but only because I like to be explicit about when objects get released. By autoreleasing the object, all we're doing is saying "this object will get released at some arbitrary point in the future." That means you can put the autoreleased object into the array, destroy the array, and the object might (probably) still exist.

With the latter method, the object would get destroyed immediately with the array (providing that nothing else has come along and retained it in the meantime). If I'm in a memory-constrained environment (say, the iPhone) where I need to be careful about how much memory I'm using, I'll use the latter method just so I don't have so many objects lingering in an NSAutoreleasePool somewhere. If memory usage isn't a big concern for you (and it usually isn't for me, either), then either method is totally acceptable.

Barnhill answered 18/7, 2009 at 15:30 Comment(1)
If you want your code to be absolutely safe it is strongly recommended to used autorelease: See #1148285Bouffard
D
3

They are both correct but B may be preferred because it has no overhead at all. Autorelease causes the autorelease pool to take charge of the object. This has a very slight overhead which, of course, gets multiplied by the number of objects involved.

So with one object A and B are more or less the same but definitely don't use A in scenarios with lots of objects to add to the array.

In different situations autoreleasing may delay and accumulate the freeing of many objects at the end of the thread. This may be sub-optimal. Take care that anyway autoreleasing happens a lot without explicit intervention. For example many getters are implemented this way:

return [[myObject retain] autorelease];

so whenever you call the getter you add an object to the autorelease pool.

Doublepark answered 18/7, 2009 at 15:29 Comment(2)
"Autorelease causes the autorelease pool to take charge of the object." This is incorrect. The autorelease pool is nothing more than a delayed messaging facility. The autorelease pool doesn't "take charge" of an object, it only lists it to receive a -release message in the future.Snitch
With "take charge" I meant exactly that. I was born in EU but not in UK, my fault.Doublepark
M
0

You can send the autorelease message at any point, because it isn't acted on until the application's message loop repeats (i.e. until all your methods have finished executing in response to user input).

http://macdevcenter.com/pub/a/mac/2001/07/27/cocoa.html?page=last&x-showcontent=text

Mendicity answered 18/7, 2009 at 15:30 Comment(0)
B
0

You have alloc'ed the object, then it's your job to release it at some point. Both code snippets work merely the same, correct way, with the autorelease way being the potentionally slower counterpart.

Personally speaking, I prefer the autorelease way, since it's just easier to type and almost never is a bottleneck.

Brawl answered 18/7, 2009 at 15:30 Comment(0)
T
0

They're both OK. Some people will tell you to avoid autorelease because of "overhead" or some such thing, but the truth is, there is practically no overhead. Go ahead and benchmark it and try to find the "overhead." The only reason you'd avoid it is in a memory-starved situation like on the iPhone. On OS X, you have practically unlimited memory, so it isn't going to make much of a difference. Just use whichever is most convenient for you.

Typhus answered 18/7, 2009 at 15:39 Comment(2)
It's not as cut-and-dried as that. If objects are being autoreleased in a loop, it's possible for large numbers of extra objects to get left around until an enclosing autorelease pool is drained. This can sometimes cause major memory overhead and slowdowns. (Yes, it has happened to me.) This isn't that common, though. Usually you can ignore the issue until it happens, and then look at what's going on in the loop and optimize it by using explicit releases.Quean
All that requires is special management of the autorelease pool. It still doesn't mean autorelease is a bad thing, especially not in the general case.Typhus
D
0

I prefer A (autoreleasing) for brevity and "safety", as johne calls it. It simplifies my code, and I've never run into problems with it.

That is, until today: I had a problem with autoreleasing a block before adding it to an array. See my stackoverflow question: [myArray addObject:[[objcBlock copy] autorelease]] crashes on dealloc'ing the array (Update: Turns out the problem was elsewhere in my code, but still, there was a subtle difference in behavior with autorelease…)

Divided answered 2/5, 2011 at 8:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.