views:

1472

answers:

6

My understanding is that both of these create a NSMutableString, only the first one is owned by the system and the second one is owned by me (i.e. I need to release it). Is there any particular reason why I should use one or the other, on the face of it it seems easier to use the first? Also is the first better as it gives the compiler a sense of size?

NSMutableString *newPath = [NSMutableString stringWithCapacity:42];

OR

NSMutableString *newPath = [[NSMutableString alloc] init];

EDIT ... ALSO

I see a lot a declarations written on two lines (i.e.)

NSMutableString *newPath;
newPath = [NSMutableString stringWithCapacity:42];

Personally I prefer the one-liner, is this just another example personal style?

+3  A: 

The former is not for the compiler necessarily, but rather a suggestion to the string as to how it might be able to optimize storing its data. This is most useful for NSDictionary/NSArray/NSSet, which have the ability to internally change their implementations depending on the size of their data set.

Other than that, you're correct: the only difference is a matter of ownership. I almost never use the WithCapactiy methods, and rather just use [NSMutableString string] or [NSMutableArray array], but IMO, it's really just a matter of style and you won't gain or lose anything by using one over the other.

Dave DeLong
So personally you would go for the alloc and do the release yourself?
fuzzygoat
@fuzzygoat - It depends. I may use one or the other based on memory constraints, whether I'm writing terse code, etc.
Dave DeLong
+1  A: 

You bring up valid questions. It really depends on what you are doing but for general iPhone apps I would say just use the first one. This will be automatically cleaned up for you when the reference count gets to 0 and you don't have to worry about it.

Use the second one when you really have a good reason to manage the string's memory yourself. Such as you want to be sure of when the string should be cleaned up, or you expect memory to be minimal at a certain time.

I would say as a general rule use the second one when you have a good reason to do so.

Ralph
+2  A: 

The first one is an autoreleased string. This will be released by the system at an appropriate point. It is added to the autorelease pool and memory will be handled by the system. Once it is out of scope, you cannot garuntee that it will be valid. This type is useful if it only has scope within your method, and also for returning values from methods.

The second is retained, so will have a reference count of 1 and is not added to the autorelease pool. You are responsible for releasing it and freeing up the memory. Use this method if you want to control the scope of the object. Used for member variables etc.

I believe the 2 line initialisation is just style, but I would not use the 2 line variation as you are defining the variable without assinging a value to it, even though you are on the next line. I guess this kind of mirrors member variable declaration/initialisation, but I dont personally like it much.

Xetius
A: 

You're correct on all your points!

I'm not sure how big a difference the size/capacity hint makes, but more information should certainly allow the run-time make better decisions.

Why use one style over the other? Well, when are autoreleased objects released? There are two non-obvious reasons why it might matter. First, when a method uses a lot of memory that you can release immediately. (You could also use a local autorelease pool I guess.) Secondly, I find that using autorelease can hide memory leaks and make debugging some code more difficult. Your mileage may vary depending on the age and quality of the code.

When I first started developing iPhone apps I used autoreleased objects all the time. It was convenient because I didn't fully understand how it all worked and it usually did the right thing. These days I tend to err on the side of manually deallocating memory. It really isn't that hard when you actually understand how the reference counting works and forces the issue immediately when you don't.

Stephen Darlington
“Secondly, I find that using autorelease can hide memory leaks and make debugging some code more difficult.” I find the opposite. If you don't release something immediately (as in `[[[… alloc] init] autorelease]`), it's **very** easy to forget the `release` message later.
Peter Hosey
Also, even if you remember to write the `release` message, it's easy to not hit it. Two ways are early returns (`if (!success) return NO;`) and thrown or propagated exceptions.
Peter Hosey
I deliberately phrased it as "I find" as I appreciate that this is entirely a personal preference. I make fewer mistakes this way as it forces me to consider the lifecycle of a variable when I write the code. Your mileage, as I said, may vary.
Stephen Darlington
A: 

If you're pretty sure how long a string you'll need, go ahead and use the -initWithCapacity: method. When you exceed a string's storage, it gets reallocated and copied, which isn't a cheap operation.

NSResponder
+7  A: 
NSMutableString *newPath = [NSMutableString stringWithCapacity:42];

OR

NSMutableString *newPath = [[NSMutableString alloc] init];

Is there any particular reason why I should use one or the other, on the face of it it seems easier to use the first?

Yes. Always autorelease immediately unless you have a specific reason not to.

The first reason is that it's very easy to forget to write the release message. If you autorelease the object in the very same statement where you create it (as in [[[… alloc] init] autorelease]), it's much more difficult to forget it and much more obvious when you do. The convenience factory methods (such as stringWithCapacity:) autorelease the object for you, so just as when you autorelease it yourself, you don't have to worry about releasing it later.

Second, even if you do remember to write the separate release message, it's easy to not hit it. Two ways are early returns:

NSString *str = [[NSString alloc] initWithString:@"foo"];

BOOL success = [str writeToFile:path atomically:NO];
if (!success)
    return;

[str release];

and thrown or propagated exceptions:

NSString *str = [[NSString alloc] initWithString:@"foo"];

//Throws NSRangeException if str is not in the array or is only in the array as the last object
NSString *otherStr = [myArray objectAtIndex:[myArray indexOfObject:str] + 1];

[str release];

The “specific reason not to” is generally that you have a tight loop that creates a lot of objects, in which case you may want to manually manage as many of the objects in the loop as you can, in order to keep your object count down. However, only do this if you have evidence that this is your problem (be it hard numbers from Shark, hard numbers from Instruments, or your system going into paging hell whenever that loop runs long enough).

Other, possibly better, solutions include splitting the loop into two nested loops (the outer one to create and drain an autorelease pool for the inner loop) and switching to NSOperation. (However, make sure you set a limit on how many operations the queue runs at a time—otherwise, you may make it even easier to go into paging hell.)

Also is the first better as it gives the compiler a sense of size?

It is better, but not for that reason.

To the compiler, it's just another class message. The compiler does not know or care what it does; for all it knows and cares, stringWithCapacity: is the message to play a song to the user.

It does give NSMutableString a size hint—the class will know how much character storage it may want to initially allocate. Whatever benefit you get from this is probably small (at least on the Mac), but if you have the information handy, why not use it? Conversely, I wouldn't go out of my way to compute it.

I see a lot a declarations written on two lines (i.e.)

NSMutableString *newPath;
newPath = [NSMutableString stringWithCapacity:42];

Personally I prefer the one-liner, is this just another example personal style?

Yes. However, there is a certain amount of risk in leaving a variable uninitialized. Definitely turn on the “Run Static Analyzer” build setting if you decide to make a habit of this.

Peter Hosey
+1, except that I disagree with "Always autorelease immediately unless you have a specific reason not to." Your justification for this ("it's very easy to forget to write the `release` message") isn't as relevant now that the static analyzer will easily catch this error. I personally avoid autoreleased objects, just to avoid autorelease pool clutter and the hit that can come from draining a very large autorelease pool (which yes, I have noticed). I generally prefer to amortize the cost of deallocation by performing it as soon as reasonably possible.
Dave DeLong
The static analyzer can take awhile to run on some source code, so you may not want it on all the time. And most of the expense of draining an autorelease pool is in releasing the objects—which you have to do anyway. The only reason it stands out is because you release them all (or a few thousand of them) at once instead of a few milliseconds apart.
Peter Hosey
DO NOT release or autorelease until the end of the method. Any message after the -autorelease can potentially empty the autorelease pool. If you're returning an autoreleased object, you don't want any chance for it to be destroyed before the caller gets it.
NSResponder
Interesting. Is this purely theoretical, or is there a real, working (i.e., crashing) example case that doesn't grossly mishandle autorelease pools?
Peter Hosey