Is there some literal dictionary or array syntax in Objective-C?

AlBlue picture AlBlue · Mar 14, 2012 · Viewed 24.2k times · Source

It's always been possible to create NSArrays (and NSDictionaries/NSNumber) with vararg method calls, like:

[NSArray arrayWithObjects: @"a", @"b", @"c", nil];

Can these be created with in-line literals in a new improvement to LLVM and Clang?

Answer

AlBlue picture AlBlue · Mar 14, 2012

With this change to the LLVM codebase, Apple has added a new syntax for literals in upcoming versions of the Clang compiler.

Before, arrays were created using a C-based array and were converted on the fly into Objective-C objects, such as:

NSArray* array = [NSArray arrayWithObjects: @"One", @"Two", @"Three", nil];

Note that since this is a varargs element, you had to supply an ending 'nil' at the end of the list. However, now there's an easier way:

NSArray* array = @[ @"One", @"Two", @"Three" ];

Note that the leading @ before the [] is required, to distinguish between it and an and ordinary C array (or a message send). Note also that the trailing 'nil' is no longer required.

A similar change has been made for in-line dictionary literals, similar to JSON structures:

NSDictionary* dict = @{
    @"Key1": @"Value1",
    @"Key2": @"Value2",
};

Finally, a new literal for NSInteger (etc.) has been added:

NSNumber* value = @3.141;

Note that although this works for floating point (@3.141F) and doubles (@3.141) it does not work for long doubles as these are not supported for wrapping by the compiler. Thus, @3.141D will be a compile-time error.

Owing to how the constants are defined, @INT_MAX is a valid valid value but @INT_MIN is not, since the latter is defined via a compile-time expression and not a literal in itself.

There are also extensions to boolean types:

NSNumber* yes = @YES;         // [NSNumber numberWithBool:YES]
NSNumber* no = @NO;           // [NSNumber numberWithBool:NO]
NSNumber* trueBool = @true;   // [NSNumber numberWithBool:(BOOL)true]
NSNumber* falseBool = @false; // [NSNumber numberWithBool:(BOOL)false]

This change has also introduced the __objc_yes and __objc_no literals to support the parsing of the types via literal value only. Their use is guarded with #if __has_feature(objc_bool) in the preprocessor, but developers should continue to use YES and NO in code.

Finally, both arrays and dictionaries can now be subscripted with array brackets, in use both as lvalue and rvalue expressions:

NSMutableArray* stuff = ...
id first = stuff[0];
stuff[0] = anotherObject;

NSMutableDictionary* moreStuff = ...
id conference = moreStuff[@"NSConf"]
moreStuff[@"SponsoredBy"] = @"NSConfDuck"

The array style subscripting (using an NSUInteger) is mapped to objectAtIndexedSubscript: and the corresponding setObject:atIndexedSubscript:, whilst the dictionary access is accessed with objectForKeyedSubscript: and setObject:forKeyedSubscript:

The full syntax for the literals can be seen at the Clang/LLVM website

Note that since this answer was initially written, Clang has added support for non-literal Objective-C expressions called 'Boxed expressions'

This means that one can use @(3+4) as an equivalent to @7, and @("Hello World") as @"Hello World". Note that a C expression which evaluates to null will result in an exception, and arguments such as @(null) are treated as a compile-time error.

It is also possible to use 'Boxed enums' for types with a known type, so

enum { North, South, East, West, };

can be placed into a boxed enum type with @(North), which will have the value 0.

Boxed expressions will be available in clang 3.2 onwards. It can be tested for using the __has_feature(objc_boxed_expressions) preprocessor test.