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

三世轮回 提交于 2019-11-29 08:11:05

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.

There's no "best way". Use whichever is the best for a particular use case. For example, if you want your app to be portable (i. e. the logic that requires Foundation only and not UIKit can run on other platforms as well, like Mac OS X or Linux with GNUstep, etc.) then avoid using the literal syntax - they're not very portable. If you need it to work on iOS only, then use them, because they're convenient.

Also, these notations are only syntactic sugar - that is, they map to method names (as far as I know, exactly to the two methods you mentioned in your question), so they don't have any effect on performance, the behavior of the algorithm, etc.

And yes, you guessed it right: the same applies to the new subscripting syntax - for NSArray, it invokes - objectAtSubscriptedIndex:.

You can use them on GNU/Linux with GNUstep and clang. In most of my cases GNUstep works with clang much better than all versions of gcc. (Sorry I should just edit the other answer, I am new to this)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!