Defining NSMutableString?

fuzzygoat picture fuzzygoat · Sep 28, 2009 · Viewed 13.4k times · Source

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?

Answer

Peter Hosey picture Peter Hosey · Sep 28, 2009
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.