I disagree with the other answers posted thus far: almost all the time, it's better to use the new container literal syntax than to use constructors. They help with code correctness, and there's not really that much to worry about for compatibility.
Code Correctness
Container literals are indeed syntactic sugar, but specifically they map to the "safe" constructor methods +[NSArray arrayWithObjects:count:]
and +NSDictionary dictionaryWithObjects:forKeys:count:
. Constructing an array or dictionary using one of these methods directly isn't all that convenient, so many programmers find it simpler to use arrayWithObjects:
and dictionaryWithObjectsAndKeys:
. However, the latter methods have a nasty pitfall: since the argument list must be terminated with nil
, you can find yourself with unexpected array/dictionary contents if you pass nil
where you intend to pass an object.
For example, say you're setting up a dictionary mapping the properties of one of your model objects (maybe you're going to send it as JSON?):
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
person.name, @"name", person.title, @"title", person.address, @"address",
nil];
If this code runs into a Person
for whom no title
has been set, the resulting dictionary will be missing the @"address"
key and its value. You could spend hours tracking down why some fraction of the people in your database are missing addresses (and even see the code above and tear your hair out wondering why it's not working when c'mon, I'm setting it right there!). Many of us have.
By contrast, if you use the literal form like this:
NSDictionary *dictionary = @{
@"name": person.name, @"title": person.title, @"address": person.address };
It will be expanded to something like this:
id objects[] = { person.name, person.title, person.address };
id keys[] = { @"name", @"title", @"address" };
NSUInteger count = sizeof(objects) / sizeof(keys);
NSDictionary *dictionary = [NSDictionary dictionaryWithObjects:objects
forKeys:keys
count:count];
And if person.name
or person.title
returns nil
, this method will throw an exception instead of silently creating data you don't want. (Either way you'll have to decide how you want your code to handle nil
titles, but this way you'll catch the problem sooner.) And sure, you could write this "safer" form yourself instead of using the equivalent syntactic sugar, but are you sure you won't just fall back on the habit of writing dictionaryWithObjectsAndKeys:
because it's shorter?
Compatibility
The code generated by container literals (and number literals and boxed expressions, for that matter) uses no new API, so you can compile it with Xcode 4.4 or newer (or Clang 3.1 or newer directly) and deploy to any version of Foundation. You do need to consider compatibility if your source code will also be used with older compilers or GNUStep, however. (Though it sounds like GNUStep is good with Clang now, too.)
And it's not part of the question, but since it's on a related subject: the same is "sort of" true for the new object subscripting syntax. That does use new methods only defined on Mac OS X 10.6 and iOS 6.0... but those methods are provided by libarclite
. (You know, the library that gets linked in when you try to deploy ARC code back to iOS 4.3 or Mac OS X 10.6 -- it's not just for ARC anymore!) So all you need to do is declare them in a header, link ARCLite if you're not already, and you're good to go.
NSNumber * numberWithInt = @7; NSNumber * numberWithBool = @YES;
– Whistling