Search

An Objective-C Literal Subtlety

Adam Preble

3 min read

Nov 28, 2012

An Objective-C Literal Subtlety

Update: In the time since this blog post was published, the compiler behavior demonstrated has been remedied in the version of clang accompanying Xcode 4.6. For a BOOL generateMipmaps, @(generateMipmaps) now evaluates to kCFBooleanTrue or kCFBooleanFalse.

Earlier this year, Apple introduced new syntax for Objective-C literals—perhaps you read Mark’s early coverage of it here on the blog (Part 1Part 2). This new syntax is a great improvement for Cocoa and iOS developers: suddenly our code can be much less verbose, while remaining expressive. There are some sharp edges, however. Consider @YES, which is shorthand for [NSNumber numberWithBool:YES]. There’s also @42 and so forth, but what if we want to store the value of an expression in an NSNumber? The boxed expression helps with that: @(1 + 3). We also need to use the boxed expression for variables. For example: @(answer). There’s a subtlety to this last case, however. I was taking our OpenGL class recently and was using GLKit’s excellent GLKTextureLoader to, well, load a texture:

options = @{};
tex = [GLKTextureLoader textureWithContentsOfFile:path
                                          options:options
                                            error:&error];

Then I decided I might want to generate mipmaps for my texture.

BOOL generateMipmaps = YES;
options = @{ GLKTextureLoaderGenerateMipmaps : @(generateMipmaps) };
tex = [GLKTextureLoader textureWithContentsOfFile:path
                                          options:options
                                            error:NULL];

Alas, the mipmaps were nowhere to be found. After entirely too much searching for problems loading mipmaps, I started poking at my assumptions. On a whim I tried simplifying my options dictionary:

options = @{ GLKTextureLoaderGenerateMipmaps : @YES };

Suddenly, I had mipmaps, but more importantly I learned that

@(YES) has a different result from @YES. Using po (Class)[obj class] I was able to find that @(YES) has a class of _NSCFNumber, while @YES is an __NSCFBoolean. So why is -textureWithContentsOfFile:options:error: interpreting my __NSCFNumber for that key as NO? According to the documentation, the value for the key GLKTextureLoaderGenerateMipmaps is “an NSNumber object that specifies a boolean value”. NSNumbers simply hold a scalar, right? And [[NSNumber numberWithInt:YES] boolValue] == [[NSNumber numberWithBool:YES] boolValue]. Why didn’t -textureWithContentsOfFile:options:error: agree? I reasoned that I could take advantage of Objective-C’s message passing and log all messages sent to my NSNumber instance. I pulled out dtrace and wrote a script – the subject of another blog post – and found, to my surprise, that -textureWithContentsOfFile:options:error: wasn’t sending _any messages to my NSNumber! After conferring with my fellow nerds we found a probable explanation: the NSNumber’s value is likely being checked using something like the following:

NSNumber *generateMipmaps =
    [options objectForKey:GLKTextureLoaderGenerateMipmaps];
if (generateMipmaps == kCFBooleanTrue)
{
    // generate those mipmaps
}

That would explain why no messages are sent to our number object, and why this method displays a distinction between

[NSNumber numberWithInt:YES] and [NSNumber numberWithBool:YES]. As an optimization, the latter always returns the same pointer (kCFBooleanTrue). The same is true for the NO counterpart – it always returns kCFBooleanFalse. I would argue that this method should use -boolValue to check the options dictionary values, but I would also argue that this is something the compiler should be handling when it translates the literal syntax into an object. I’ve filed radars on both of these issues this with Apple (rdar://12761147 and rdar://12761621). Enjoy the new literal syntax with care, and watch out for these sorts of pitfalls in your Objective-C work. If you’re a consumer of APIs, be aware of this slippery behavior when mixing @() with YES and NO. If you’re writing an API, the extra -boolValue won’t cost that much, and you’ll save yourself emails from confused users.

Angie Terrell

Reviewer Big Nerd Ranch

Angie joined BNR in 2014 as a senior UX/UI designer. Just over a year later she became director of design and instruction leading a team of user experience and interface designers. All told, Angie has over 15 years of experience designing a wide array of user experiences

Speak with a Nerd

Schedule a call today! Our team of Nerds are ready to help

Let's Talk

Related Posts

We are ready to discuss your needs.

Not applicable? Click here to schedule a call.

Stay in Touch WITH Big Nerd Ranch News