Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
507 views
in Technique[技术] by (71.8m points)

objective c - Should I prefer to use literal syntax or constructors for creating dictionaries and arrays?

I am reading through the iOS Developer Guide to get familiarized with the Objective-C language and currently I am having a little confusion on the topic of Container Literals and Subscript Notation as it pertains to creating objects like NSDictionary.

I understand that there are several ways to create NSDictionary objects including Key-Value encoding (dictionaryWithObjects:forKeys: and dictionaryWithObjectsAndKeys:, or their corresponding initializers). Source Link.

From my understanding there are two main ways to do this and then there is another way which is by using container literals, demonstrated here:

NSDictionary *myDictionary = @{
   @"name" : NSUserName(),
   @"date" : [NSDate date],
   @"processInfo" : [NSProcessInfo processInfo]
};

Which is the best way to use? Is there any benefit in using the Container Literal technique over the previous two or is it just a convenience thing for programmers?

I am under the impression that it is also just another easier way to code things like arrays. Is this true or is there something that I'm missing here? Are these techniques just a matter of personal preference?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...