Sunday, February 19, 2012

Associative References in Objective-C

Take the following Javascript (stick with me):

var obj = {};
obj.newProperty = "Hello World";

For the unfamiliar, we've just declared an empty object and associated the string Hello World to the key newProperty with the instance obj.

We can access the value of newProperty using dot notation:

console.log(obj.newProperty);    // logs Hello World

or as an associative array:

console.log(obj["newProperty"]); // logs Hello World

Finally, if we want to remove the property, we can use the delete statement:

delete obj.newProperty;

What has this got to do with Objective-C?

Being able to add arbitrary data to an object is a powerful tool.  It turns out that as of OS X 10.6 and iOS 3.1, Apple added new Objective-C runtime APIs, categorized as associative references to do just that.

Semantically, the following code is the same as the previous Javascript:

NSObject *obj = [NSObject new];
objc_setAssociatedObject(obj, @"newProperty", @"Hello World", OBJC_ASSOCIATION_RETAIN_NONATOMIC);

which associates the string Hello World to the instance obj using the key newProperty. The last parameter allows us to instruct the runtime how the associated instance will be transferred to obj. These policies map to regular Objective-C property attributes including copy, retain, assign and their non-atomic counterparts.

printf("%s\n", [objc_getAssociatedObject(obj, @"newProperty") UTF8String]);

Retrieves the value of newProperty from instance obj.

objc_setAssociatedObject(obj, @"newProperty", nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

Passing nil clears the association.

That's great, but why not just declare the property in my class declaration?

Fair enough, however recall Objective-C categories, which provides a means for adding methods to a class. Commonly used as an alternative to subclassing, categories are unable to add additional state, and therein lies their limitation. This example served as inspiration for the following, which provides a convenience method for displaying a UIAlertView with a completion block, all neatly contained within the same instance, No Subclassing Required ™