Cocoaphony

Stop mutating, evolve

Quit Using +stringWithString:

I keep coming across code like this:

newMonster.trueName = [NSString stringWithString:@"New Monster"];

It’s time to say stop it already with the extra +stringWithString:. I haven’t worked out yet where this anti-pattern comes from. Maybe it’s a misunderstanding of some sample code in Kochan? Maybe it’s a Java/.NET thing? I’m not sure. But I see it so often from so many places that it’s clearly something that needs discussing. (The rest of the linked article is good; it just gave me a good example of this issue.)

The correct form of the above code is:

newMonster.trueName = @"New Monster";

Now isn’t that easier? @"Blah" is an NSString object. Full object. Does everything an NSString can do:

NSString *foobar = [@"foo" stringByAppendingString:@"bar"];

It’s secretly a private subclass of NSString called NSConstantString, but that doesn’t matter for this purpose. In proper OOP, a subclass is as good as its parent. If it isn’t, then the model is broken.

Now, that leads us to NSMutableString. I sometimes see this code, and sometimes it’s right and sometimes it’s wrong:

NSString *string = [NSString stringWithString:mutableString];

This code is wrong if you’re going to return string. If the caller expected an NSString and you return an NSMutableString, that’s fine. There is no reason to make an extra copy just to change the type; it’s a waste of time and memory.

BUT… there’s another case. What if someone passes you an NSMutableString when you expect an NSString? If you’re going to retain that object, it’s a good idea to make a copy. That way if the caller modifies the string later, your copy doesn’t change behind your back. Consider this code:

NSMutableString *ms = [@“foo” mutableCopy]; anObject.foo = ms; [ms appendString:@“bar”]; anObject.foobar = ms;

In this case, anObject would be very surprised that its foo property suddenly held “foobar”. The fix to that is to use -copy in -setFoo:. +stringWithString: would be ok in this case, but I prefer -copy because it’s shorter, clearer to me, and almost certainly faster at least in some cases. This is why I recommend using the “copy” attribute on public properties that have well-known mutable subclasses (NSString, NSArray, NSSet, etc.)

You’ll note that I used -mutableCopy above to create the NSMutableString. I did that just to show how to use -mutableCopy. Generally for initializing an NSMutableString to a constant string, I’d use +stringWithString:, which is what this method is really for: creating an NSString subclass with an NSString. But you should never need +stringWithString: to create an NSString itself.