Looking Forward to Objective-C Literals
- New Literals / Numbers / Boxings
- Container Literals / Subscripting / Feature Tests
In all likelihood, OS X 10.8 Mountain Lion's debut remains months away. During Apple's Q2 financials conference call, company spokespersons said the upgrade would ship "late Summer" 2012. Many people are looking forward to Mountain Lion's new iOS-inspired features such as reminders and notification center. Developers, however, are promised a new set of tools currently being previewed in the Xcode 4.5 beta release.
Normally, features in a beta toolset are out of bounds due to non-disclosure agreements (NDAs). With apologies to Chuck Palahniuk, the first rule of NDA is "You don't talk about NDA material." The second rule of NDA is "You don't talk about NDA material," and so forth.
With Xcode's LLVM/clang, Apple regularly pushes documentation and source to the public llvm.org website, excluding these new features from NDA considerations. This March, Apple added an update talking about an exciting new set of Objective-C features for the clang compiler. These new Objective-C Literals introduce new ways to simplify object creation in code.
This write-up introduces some of these new features and discusses how you'll be able to use them in your code using the new compiler.
New Literals
Detailed at LLVM's website, new Objective-C literals introduce features that transform awkward constructs like NSNumber and NSArray creation instances into parsimonious expressions.
Think about how often you type cookie-cutter templates like [NSNumber numberWithInteger:5] to produce number objects in your code. Perhaps you've defined macros to simplify your coding. Speaking as someone who has long created macro definitions for the NSNumber declarations, I look forward to the way these literals provide more readable, succinct code. They save an enormous amount of typing and provide a natural, coherent presentation.
Now, instead of establishing endless series of those declarations, consider using a simple literal like @5. This number literal is just like the strings literals you've used for years. With strings, the at-sign followed by a string constant, e.g. @"hello". Clang 3.1 introduces the same kind of literal declaration to numbers and collections, an at-signed followed by a number value.
This new advance squeezes together previously wordy constructs to create simpler, more succinct representations.
Numbers
Clang's new number literals allow you to wrap scalar values like integers and floating point numbers into object containers. Just add a @-prefix to a scalar. For example, you can transform 3.1415926535 to a conforming NSNumber object as follows:
NSNumber *eDouble = @ 2.7182818;
This number literal is functionally equivalent to the following.
NSNumber *eDouble = [NSNumber numberWithDouble: 2.7182818];
The difference is that the compiler takes care of the heavy lifting for you. You don't have to use a class call and you don't have to write out a full method, brackets and all. Instead, you prefix the number with an @-sign and let clang do the rest of the work.
Standard suffixes allow you to specify whether a number is a float (F), long (L), longlong (LL), or unsigned (U). Here are some examples of how you would do that. Notice how simple each declaration is, without having to use numerous specialized method calls.
NSNumber *two = @2; // [NSNumber numberWithInt:2]; NSNumber *twoUnsigned = @2U; // [NSNumber numberWithUnsignedInt:2U]; NSNumber *twoLong = @2L; // [NSNumber numberWithLong:2L]; NSNumber *twoLongLong = @2LL; // [NSNumber numberWithLongLong:2LL]; NSNumber *eDouble = @2.7182818; // [NSNumber numberWithDouble: 2.7182818]; NSNumber *eFloat = @2.7182818F; // [NSNumber numberWithFloat: 2.7182818F];
Unfortunately you cannot wrap long double numbers according to the clang specification. Be aware that the follow statement will cause the compiler to complain. (Apple's runtime doesn't support long doubles either.)
NSNumber *eLongDouble = @2.7182818L // Will not compile
Boxings
Clang 3.1 supports only literal scalar constants after the @-sign. If you want to interpret a value and then convert it to a number object, you have to use the traditional method call:
NSNumber *two = [NSNumber numberWithInt:(1+1)];
Boxed expressions, i.e. values that are interpreted and then converted to number objects, will debut in clang 3.2. A boxed expression is enclosed in parentheses, telling the compiler to evaluate and then convert to an object e.g.
NSNumber *two = @(1+1); int foo = ...; // some value NSNumber *another = @(foo);
While 3.2-compliant, this boxed expression is not supported in clang 3.1. So you must exercise care when using constants like INT_MIN and INT_MAX. As the clang-site documentation points out, these items are defined as follows:
#define INT_MAX 2147483647 /* max value for an int */ #define INT_MIN (-2147483647-1) /* min value for an int */
Notice how INT_MAX refers to a plain literal, while INT_MIN is evaluated as an expression. @INT_MAX works in clang 3.1 but @INT_MIN will not work until clang 3.2.
There are other considerations to think about as full support for boxing rolls out. Take enums for example. While you'd think you should be able to define an enum and then use them directly, allowing user-defined sequences that start with an @ and continue with text could cause issues. Observe the following poorly chosen enum:
enum {interface, implementation, protocol};
You might imagine you could create an NSNumber with the value 2 by defining:
NSNumber *which = @protocol;
That would, quite obviously, be bad. Boxing prevents any conflict with current and future @-delimited literals:
NSNumber *which = @(protocol); // [NSNumber numberWithInt:2];
The boolean @YES and @NO, which produce number objects equivalent to [NSNumber numberWithBool:YES] and [NSNumber numberWithBool:NO] should be in a compliant 3.1 release. A caution: check for actual support in the compiler you use. Details follow later in this write-up.
Boxed expressions are not limited to numbers. They work for strings as well. The following assignment evaluates the results of strstr and forms an NSString from the results (i.e. @"World!").
NSString *results = @(strstr("Hello World!", "W"));