Search

Spelunkhead

Mark Dalrymple

7 min read

Apr 10, 2013

Spelunkhead

You can find all sorts of interesting and useful stuff in Apple’s header files. Don’t be afraid to explore them. I usually troll through the headers when a new major SDK version comes out (like IOS 7 probably will be this year) to see what’s new. I also use them for API exploration. As always, when in doubt be sure to read the official documentation. Apple’s documentation is good. It’s also voluminous. But once you’ve marinated in a framework for awhile, you know how things work and might only need a refresher or a nudge in the right direction. These days I usually spelunk in the headers, and then hit the docs if I’m not sure what the headers are trying to tell me.

T3h M0by HeAd3r

Header spelunking comes into its own when you’re not dealing with the header files on an individual basis. “I am looking at you NSArray.h. OK, now I am looking at you UIView.h”. Instead, I glom the Foundation and AppKit / UIKit headers all together into one file that I call

moby.h for mac and iphonemoby.h for iOS. “Moby” comes from the Jargon file definition, meaning “ludicrously big”. An easy way to make them is to go to the SDK directory in your Xcode bundle, such as:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/
  Developer/SDKs/iPhoneOS6.1.sdk/System/Library/Frameworks

And then accumulate the header files you want into your own moby file. If you want to glom All The Things together, check out

Tom von Schwerdtner’s script that makes a megamoby file out of all of the framework headers in an SDK. You can also download my iphone version and play along at home.

Looking for blocks in all the wrong places

So, you have all the separate headers in one place. Load them up in your favorite editor and search around. There’s a lot of neat stuff. When blocks first came out, I was really interested in seeing what API was available that used them. You can search for the hat “^” to locate them. Luckily XOR is not something commonly found in headers, so searching for the hat finds block-based stuff. Some of the calls are kind of cool, but don’t get a lot of attention, like NSDictionary’s

- (NSSet *)keysOfEntriesPassingTest:(BOOL (^)(id key, id obj, BOOL *stop))predicate

This feeds you the keys of the dictionary one by one and your block figures out if that key is interesting enough to toss in a pile. When finding block-based API, there is one block type hidden by a typedef:

typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);

Now you can search around for NSComparator and see where comparison blocks are used.

Syntax Hints

I can never remember the precise syntax for creating block-based typedefs and method arguments. I can read them fine, but can never remember if the name goes in front or the middle, or does the type go inside or outside the parenthesis? I can search for

NSComparisonResult (actually searching for ^NS is sufficient), find the typedef, and then cargo-cult from it. Same goes for blocks that are @properties. If I need to remember the syntax, I can grep the file:

% grep @property ~/moby.h | grep ^
@property (copy) void (^readabilityHandler)(NSFileHandle *)  NS_AVAILABLE(10_7, 5_0);
@property (copy) void (^writeabilityHandler)(NSFileHandle *) NS_AVAILABLE(10_7, 5_0);
...

You can combine that into one search, but my brain thinks first “reduce the file to @properties” and then “find some with a hat somewhere in the line”. If I remembered more about the syntax to write a good regular expression for it, I’d already know what I need.

Seeing New Stuff

Apple’s code ecosystem is pretty complex. You’ve got libraries that have been undergoing constant evolution for years, new calls added, and existing calls being deprecated (not “depreciated”.) One thing you can see in the headers are the availability macros, decorations used on the symbols in the headers to describe what version of Mac OS or iOS that thing appeared, and what version (if any) it was deprecated. If you want the gory details on how it works, check out

NSObjCRuntime.h. Take a look at NSOperation:

NS_CLASS_AVAILABLE(10_5, 2_0)
@interface NSOperation : NSObject {

This means that this class appeared in OS X 10.5 and iPhone OS 2.0.

- (void (^)(void))completionBlock NS_AVAILABLE(10_6, 4_0);
- (void)setCompletionBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);

Means that the operation block stuff was added in OS X 10.6, and iOS 4. Other things can be decorated with these as well. FoundationErrors.h includes some new constants:

NSPropertyListErrorMinimum NS_ENUM_AVAILABLE(10_6, 4_0) = 3840,
    NSPropertyListErrorMaximum NS_ENUM_AVAILABLE(10_6, 4_0) = 4095,
    NSXPCConnectionInterrupted NS_ENUM_AVAILABLE(10_8, 6_0) = 4097,
    NSXPCConnectionInvalid NS_ENUM_AVAILABLE(10_8, 6_0) = 4099,

The property list min/max appeared back in Snow Lion Leopard and iOS4, and XPC connection stuff added in Mountain Lion and iOS 6. The presence of XPC in iOS is intriguing. Maybe we’ll have XPC available to us in a later version iOS? That would be cool. Deprecation has its own macro.

initWithCString was deprecated back in OS X 10.4:

- (id)initWithCString:(const char *)bytes NS_DEPRECATED(10_0, 10_4, 2_0, 2_0);

The parameters to the macro are the OS X introduction version, the version it was deprecated in, and then the iOS introduction and deprecation version. Here,

initWithCString was available in iPhone OS 2, but was deprecated out of the gate, primarily existing to support legacy code brought over from the Mac side. The availability macros give you an easy, easy way to see API introduced with new versions of an OS. Say you got a developer preview of 10.8 and the rest of the world is still on 10.7. Load up the Cocoa headers and search for “10_8”. You’ll find all the calls that were added or deprecated for that OS version.

Seeing Fun Stuff

One happy side-effect of rummaging through the headers is finding something amusing, like this from NSFileManager.h:

- (void)skipDescendents;
/* This method is spelled correctly.
 */
- (void)skipDescendants NS_AVAILABLE(10_6, 4_0);

Also, what I called the “oops” macro:

APPKIT_EXTERN NSString *NSFontColorAttribute  NS_DEPRECATED_MAC(10_3, 10_4);
    // This attribute is obsolete. Use NSForegroundColorAttributeName instead.

This means that it was introduced in OS X 10.3 and deprecated in OS X 10.4. It was more fun in older versions of the headers, where it was pretty obvious what happened:

APPKIT_EXTERN NSString *NSFontColorAttribute
    AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4;
    // This attribute is obsolete. Use NSForegroundColorAttributeName instead.

Learning New Stuff

Apple’s headers are frequently well documented.

NSKeyValueObserving.h has a ton of info in it, for instance. You of course can get that from the regular documentation. What’s nice is if you live an alternative editor lifestyle, you can get a lot of useful information without having to change to another app. There are also some technologies that are only documented in the headers, such as the launchd API found in /usr/include/launch.h. Frequently, brand new technologies in Apple pre-releases only have header file documentation. Don’t forget the Core Foundation headers – some of them have interesting and useful information in them, such as this from the top of CFArray.h:

Computational Complexity
        The access time for a value in the array is guaranteed to be at
        worst O(lg N) for any implementation, current and future, but will
        often be O(1) (constant time). Linear search operations similarly
        have a worst case complexity of O(N*lg N), though typically the
        bounds will be tighter, and so on.

This is a tremendously cool window on the possible implementations of CFArray, and hence NSArray because NSArray use CFArray under the hood. If the array was purely a block of pointers you’d expect access time to be constant and linear search being

O(N). The lg N’s in there imply a possible tree structure. If the array fills up, rather than continually reallocating the array as it grows, CFArray is free to allocate a second chunk of memory and hook it into its existing storage as a tree. Finally, some cool calls in Cocoa are implemented with a preprocessor component, which you can learn from. Consider NSDictionaryOfVariableBindings, which appeared with Cocoa autolayout. You can have something like this

UIButton *snarfButton = ...;
UILabel *greebleLabel = ...;
NSDictionary *viewsDict = NSDictionaryOfVariableBindings (snarfButton, greebleLabel);

And it creates the equivalent of

@{ @"snarfButton" : snarfButton, @"greebleLabel" : greebleLabel }

That’s pretty cool. How do they do that? Here’s what it is in the headers:

#define NSDictionaryOfVariableBindings(...) _NSDictionaryOfVariableBindings(@"" # __VA_ARGS__, __VA_ARGS__, nil)
UIKIT_EXTERN NSDictionary *_NSDictionaryOfVariableBindings(NSString *commaSeparatedKeysString, id firstValue, ...) NS_AVAILABLE_IOS(6_0); // not for direct use

So,

NSDictionaryOfVariableBindings is a macro, which makes its arguments available as _VA_ARGS_. It stringifies them first with the pound-sign, which provides the dictionary keys. Then _VA_ARGS_ is used again, which provides the variables themselves. The macro constructs a call to a private function (note the leading underscore. Do Not Use this function directly), ultimately making a call that looks like

_NSDictionaryOfVariableBindings(@"snarfButton", @"greebleLabel", snarfButton, greebleLabel, nil);

Correction: Many thanks to Jesse Rusak for the correct form this takes. Rather than

#__VA_ARGS__ splitting the arguments into multiple arguments, it creates a comma-separated string. The @”” makes the compiler emit an NSString rather than a C string:

_NSDictionaryOfVariableBindings(@"" "snarfButton, greebleLabel", snarfButton, greebleLabel, nil);

It’s then simple to take these arguments and make a dictionary out of them.

Dat’s All

I hope I’ve whetted your appetite for exploration and learning via the headers. Do be careful about basing your own code on assumptions on how things are implemented by what you see in the headers. UIView may contain some interesting bit fields you could whack to make some of your own code easier, but Apple can (and sometimes does) change the implementation details.

Got any favorite finds from the headers? Share ‘em in the comments!

Mark Dalrymple

Author Big Nerd Ranch

MarkD is a long-time Unix and Mac developer, having worked at AOL, Google, and several start-ups over the years.  He’s the author of Advanced Mac OS X Programming: The Big Nerd Ranch Guide, over 100 blog posts for Big Nerd Ranch, and an occasional speaker at conferences. Believing in the power of community, he’s a co-founder of CocoaHeads, an international Mac and iPhone meetup, and runs the Pittsburgh PA chapter. In his spare time, he plays orchestral and swing band music.

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