Friday, February 24, 2012

Something wonderful: new Objective-C literal syntax

mountain lion cub going rawrrrrrWith the announcement of OS X 10.8 Mountain Lion, Apple made the beta version of Xcode 4.4 and the Mountain Lion SDK available to developers enrolled in the Mac Developer Program (the US$99 annual membership required to submit apps to the Mac App Store). The new OS X version has created a lot of developer buzz around Gatekeeper, the upcoming requirement that all apps in the Mac App Store run in a restrictive sandbox similar to iOS apps. This is a big problem for makers of development tools and other specialized apps that need low level access to the system; Apple has effectively banned many of them from the App Store. On the other hand, strong argument can be made that Gatekeeper and app sandboxing will be a big win for most non-technical Mac users.

The news about Mountain Lion isn't all bad for developers. Apple keeps their beta SDKs under a non-disclosure agreement, but with developer interest high for both iOS and OS X, details tend to leak out. CocoaHeads recently revealed that the Apple LLVM 4.0 compiler in the Xcode 4.4 beta includes support for an Objective-C literal syntax for NSDictionary, NSArray and NSNumber objects.

If you've done any Objective-C programming, you've seen the NSString literal syntax:
// NSString literal
NSString *name1 = @"Lana Kane";

// creating NSString from a C string literal
NSString *name2 = [NSString stringWithCString:"Sterling Archer"
encoding:NSUTF8StringEncoding];

An NSString literal is like a plain old C string literal, but prefixed with the '@' character. This has made using NSString objects easy, such as adding them to a NSArray. Using numbers, however, meant lots of tedious calls to [NSNumber numberWithXxx:...]. Like NSString literals, the new NSNumber literals are simply the plain old C number literals prefixed with '@'. This included character literals (since characters are simply small integers of type char) as well as the BOOL values YES and NO (though technically YES and NO are #define'd constants, not literals).
// new NSNumber literals
// and old style NSNumber creation
NSNumber *intNumber1 = @42;
NSNumber *intNumber2 = [NSNumber numberWithInt:42];

NSNumber *doubleNumber1 = @3.1415926;
NSNumber *doubleNumber2 = [NSNumber numberWithDouble:3.1415926];

NSNumber *charNumber1 = @'A';
NSNumber *charNumber2 = [NSNumber numberWithChar:'A'];

NSNumber *boolNumber1 = @YES;
NSNumber *boolNumber2 = [NSNumber numberWithBool:YES];

The new syntax supports the usual number suffixes like 'f' for float and 'u' for unsigned number types.
NSNumber *unsignedIntNumber1 = @256u;
NSNumber *unsignedIntNumber2 = [NSNumber numberWithUnsignedInt:256u];

NSNumber *floatNumber1 = @2.718f;
NSNumber *floatNumber2 = [NSNumber numberWithFloat:2.718f];

Having a compact NSNumber literal makes using numbers in collections much nicer. Here's a NSArray with some strings and numbers:
// an array with string and number literals
NSArray *array1 = [NSArray arrayWithObjects:@"foo", @42, @"bar", @3.14, nil];

// and the old way
NSArray *array2 = [NSArray arrayWithObjects:@"foo",
[NSNumber numberWithInt:42],
@"bar",
[NSNumber numberWithDouble:3.14],
nil];

This would be nice all by itself, but really shines when combined with the new NSArray and NSDictionary literals. Here is the array from above using the new NSArray literal syntax:
// an array literal
NSArray *array1 = @[@"foo", @42, @"bar", @3.14];

Like many popular languages today, NSArray literals use the square bracket characters '[' and ']' to delimit the array literal; the only twist here is you prefix it with '@' symbol. And thankfully, you no longer need to remember to put nil at the end!

And there's a similar notation for NSDictionary literals:
// a dictionary literal
NSDictionary *dictionary1 = @{ @1: @"red", @2: @"green", @3: @"blue" };

// old style
NSDictionary *dictionary2 = [NSDictionary dictionaryWithObjectsAndKeys:@"red", @1,
@"green", @2,
@"blue", @3,
nil];

The curly brace characters '{' and '}' delimit the dictionary literal and the colon ':' character to separate keys and values, similar to widely used languages like Python and JavaScript, but of course prefixed with '@'. I was never a fan of the backwards "ObjectsAndKeys" way of creating a NSDictionary, and I'm glad to have a much more compact alternative.

This new syntax is basically what Ole Begemann proposed in his blog in 2010 (EDIT: and Stig Brautaset proposed in his blog in 2008, and by others even earlier) and it's great to see that Apple is actively eventually listening to their developer community and finding real ways to make Objective-C a nicer language. Along with the @property syntax and ARC, this goes a long way toward reducing boilerplate code. Objective-C's dynamic object model, inspired by Smalltalk, has always made it more akin to dynamic languages like Python and Ruby than static ones like C++ and Java. ARC and compact container literals are big steps that take Objective-C further into dynamic language territory, yet the low level power of C is always available when you need it. I can't wait until this is released.

Update: Apple has committed a patch with the new syntax to the LLVM project.

6 comments:

Andrea said...

Be a superset of C give a great advantage to Objetive-C.
..is a great news, thanks for the write up!

iOS Cognitive Designer & Developer
www.andreapicchi.it

Faceleg said...

I absolutely cannot _wait_ until I am able to start using this!

nicktoumpelis said...

I'm really looking forward to this. It's a pity that Xcode 4.3 doesn't have this.

skugg said...

I wished for this back in 2008:

http://skuggdev.wordpress.com/2008/08/25/objective-c-syntax-sugar-wish-list/

It was apparently old news already then, because the radar I filed was closed as a duplicate. :)

Don McCaughey said...

@skugg: I guess great minds think alike :-p

@Apple: why did this take so long? Still, I'm glad to have it.

Dave Higgins said...

In your example for NSDictionary literals... For the // old style example, shouldn't the keys be old style, too?

......dictionaryWithObjectsAndKeys:
@"red", [NSNumber numberWithInt:1],
@"green", [NSNumber numberWithInt:2],
@"blue", [NSNumber numberWithInt:3], nil];