Is it better (faster & more efficient) to use alloc
or autorelease
initializers. E.g.:
- (NSString *)hello:(NSString *)name {
return [[NSString alloc] initWithFormat:@"Hello, %@", name];
}
OR
- (NSString *)hello:(NSString *)name {
return [NSString stringWithFormat:@"Hello, %@", name];
// return [@"Hello, " stringByAppendingString:name]; // even simpler
}
I know that in most cases, performance here shouldn't matter. But, I'd still like to get in the habit of doing it the better way.
If they do exactly the same thing, then I prefer the latter option because it's shorter to type and more readable.
In Xcode 4.2, is there a way to see what ARC compiles to, i.e., where it puts retain
, release
, autorelease
, etc? This feature would be very useful while switching over to ARC. I know you shouldn't have to think about this stuff, but it'd help me figure out the answer to questions like these.
The difference is subtle, but you should opt for the
autorelease
versions. Firstly, your code is much more readable. Secondly, on inspection of the optimized assembly output, theautorelease
version is slightly more optimal.The
autorelease
version,translates to
Whereas the [[alloc] init] version looks like the following:
As expected, it is a little longer, because it is calling the
alloc
andinitWithFormat:
methods. What is particularly interesting is ARC is generating sub-optimal code here, as it retains thename
string (noted by call to _objc_retain) and later released after the call toinitWithFormat:
.If we add the
__unsafe_unretained
ownership qualifier, as in the following example, the code is rendered optimally.__unsafe_unretained
indicates to the compiler to use primitive (copy pointer) assignment semantics.as follows:
Comparing the performance of the two is a bit of a moot issue for a couple of reasons. First, the performance characteristics of the two might change as Clang evolves, and new optimisations are added to the compiler. Second, the benefits of skipping a few instructions here and there are dubious at best. The performance of your app should be considered across method boundaries. Deconstructing one method can be deceiving.
I think that the stringWithFormat: implementation is actually implemented just as your 1st version, which means nothing should change. In any case, if there is any difference, it probably seems as the second version should not be slower. Finally, in my opinion the second version is slightly more readable, so that's what I'd use.
Well, this is something easy to test, and indeed it seems the convenience constructor is "faster" -- unless I made some error in my test code, see below.
Output (Time for 1 Million constructions)
Script
[NSString stringWithFormat:]
is less code. But be aware that the object may end up in the autorelease pool. And that currently happens even with ARC and -Os compiler optimization.Currently the performance of
[[NSString alloc] initWithFormat:]
is better on both iOS (tested with iOS 5.1.1 and Xcode 4.3.3) and OS X (tested with OS X 10.7.4 and Xcode 4.3.3). I modified @Pascal's sample code to include the autorelease pool drain times and got the following results:[[NSString alloc] initWithFormat:]
is around 14% faster on iPhone 4S, and around 8% faster on OS XHaving an @autoreleasepool around the loop releases all objects at the and of the loop, which eats up a lot of memory.
I disagree with the other answers, the autorelease version (your 2nd example) is not necessarily better.
The autorelease version behaves just as is it did before ARC. It allocates and inits and then autoreleases, which means the pointer to the object needs to be stored to be autoreleased later the next time the autorelease pool is drained. This uses slightly more memory as the pointer to that object needs to be kept around until it is processed. The object also sticks around longer than if it was immediately released. This can be an issue if you are calling this many times in a loop so the autorelease pool would not have a chance to be drained. This could cause you to run out of memory.
The first example behaves differently than it did before ARC. With ARC, the compiler will now insert a "release" for you (NOT an autorelease like the 2nd example). It does this at the end of the block where the memory is allocated. Usually this is at the end of the function where it is called. In your example, from viewing the assembly, it seems like the object may in fact be autoreleased. This might be due to the fact the compiler doesn't know where the function returns to and thus where the end of the block is. In the majority of the cases where a release is added by the compiler at the end of a block, the alloc/init method will result in better performance, at least in terms of memory usage, just as it did before ARC.