Adding Properties to a Category Using Associated Objects

Categories are a cool feature of Objective-C that allow you to extend existing classes with extra methods. This helps you to write really clean code by adding utility methods directly to the classes they relate to, so they can be called in a natural, object-oriented way.

A typical example of a category method is given below. Here we add an isNumeric method to NSString:

@interface NSString (NumberUtils)
 
- (BOOL)isNumeric;
 
@end
 
@implementation NSString (NumberUtils)
 
- (BOOL)isNumeric
{
    NSScanner *scanner = [NSScanner scannerWithString:self];
    return [scanner scanFloat:NULL]? [scanner isAtEnd]: NO;
}
 
@end

You can even declare new properties in a category, but you can’t synthesize them. That’s because it isn’t possible to add new ivars to a class via a category – the internal data structure of the class is already decided at compile time, and categories (which are loaded later, at runtime) can’t change it.

You can only create virtual properties – getter/setter methods that have the same semantics as an ordinary property but are not backed by an ivar. For example, we could have declared our isNumeric method above as a readonly property, like this:

@interface NSString (NumberUtils)
 
@property (nonatomic, readonly, getter=isNumeric) BOOL numeric;
 
@end

That lets us use property syntax to call methods, but it doesn’t help us if we want to store extra data in our class that cannot be derived from existing properties.

Suppose we have an app where the user can tag images, and we want to store the tag value as part of each UIImage object. The tag will be a comma-delimited string. We can declare the tag property in a category, but how will the setter and getter methods work if we cant use an ivar to store the string?

(Before you ask, yes, the right way to do this would be to create a wrapper object containing both an image and the tag value, but let’s suppose for the sake of this example that we don’t have time to refactor all our image handling methods to accept a new object type, and the boss is breathing down our necks!)

Runtime to the Rescue

Cocoa’s high-level APIs are so powerful that many developers never venture much below the surface. That’s a shame, because the Objective-C runtime kicks ass. One really neat feature of the runtime is that it lets you extend an object with additional data using a mechanism called associated objects.

As the name suggests, associated objects are values that you associate with another object, using a unique key to store and retrieve them, much like a dictionary. You can get and set these associated objects using the following two runtime functions, and they will be automatically released when the object they are associated with is deallocated:

// Get a value associated with the object by key
id objc_getAssociatedObject(id object, const void *key)
 
// Associate a value with the object by key
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

The object argument is the object that is being extended. The key is a unique identifying value that is must remain constant for the lifetime of the process. You can use an NSString as the key, but it won’t work as expected if you try to access the value using a string object with identical text but a different memory address. A better approach is to declare a private static pointer variable to use as the key (more on this later).

The policy argument is a constant that defines how the object will be stored. This works a lot like the atomic/nonatomic and retain/copy/assign modifiers that you use when declaring properties.

Here is how we would implement our tagged image category using associated objects. First we declare the tag property in our category header:

@interface UIImage (Tagged)
 
@property (nonatomic, copy) NSString *tag;
 
@end

Then we implement the setter and getter for the property using the associated object runtime methods:

#import <objc/runtime.h> 
 
static const void *ImageTagKey = &ImageTagKey;
 
@implementation UIImage (Tagged)
 
- (void)setTag:(NSSting *)tag
{
    objc_setAssociatedObject(self, ImageTagKey, tag, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
 
- (NSString *)tag
{
    return objc_getAssociatedObject(self, ImageTagKey);
}
 
@end

There are a couple of things to note here:

  • The key we are using for our associated object is a pointer of type static const void *. We have to initialise this pointer with something, otherwise its value will be NULL (making it useless as a key), but we don’t care what value it points to, so long as it’s unique. To avoid allocating any unnecessary additional memory, we’ve set it to point to itself! The value of the pointer is now the same as the address of the pointer, which is unique (because it’s static), and won’t change (because it’s const).
  • We’re using OBJC_ASSOCIATION_COPY_NONATOMIC as the association policy. This matches the attributes of the tag property we declared in the category header. We’re using nonatomic because this is a UIKit class and we’re assuming it will only be accessed on the main thread. We’re using copy because this is always best practice when dealing with strings, or any other type that has mutable subclasses (to ensure that the value we store is actually an NSString and not an NSMutableString).

So there you have it – you can now easily add extra properties to existing classes!

Selectors as Keys

When creating a lot of category properties, it can be a little annoying to have to create all those static const keys. It turns out that there is a neat alternative:

Objective-C selectors (method names) are actually constant pointers. This means that they are suitable to be used as keys for associated objects. If you are using associated objects to implement a property then you can use the property’s getter method name as the key. In our tag example, that would look like this:

#import <objc/runtime.h> 
 
@implementation UIImage (Tagged)
 
- (void)setTag:(NSSting *)tag
{
    objc_setAssociatedObject(self, @selector(tag), tag, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
 
- (NSString *)tag
{
    return objc_getAssociatedObject(self, @selector(tag));
}
 
@end

There’s no particular advantage to this approach, other than that it makes the code cleaner. The only potential disadvantage is that it makes it easier to access the associated object externally from the class. Static keys are truly private, but @selectors are public. Since we’re using the associated object to implement a public property in this case anyway, that doesn’t really matter.

A Word of Caution

There are some classes of object that you should not attempt to associate other objects with:

For value types (immutable, data-carrying objects such as NSString, NSNumber, NSValue, UIColor, NSIndexPath, and so on), iOS uses a process called de-duplication to reduce memory consumption and improve performance. De-duplication means replacing multiple identical objects with a single instance. In the case of NSString this means that two strings with the same text may end up being silently mapped to the same object behind the scenes, even if they were initialised separately.

Normally, de-duplication doesn’t affect your code because you don’t care whether two NSStrings or NSNumbers that have the same value are the same object or not. That’s because you always compare them with isEqual: instead of == (or if you don’t, you should). But when you associate an object with a value type such as NSString, you may find that the string you associated it with has suddenly been replaced with another instance, or that another unrelated string variable has inherited the same association.

It’s unlikely that in normal use you would want to associate object with value types anyway, but it’s something to bear in mind.

Additional Reading

Check out the Apple Objective-C Runtime Reference for further information:


Nick Lockwood is the author of iOS Core Animation: Advanced Techniques. Nick also wrote iCarousel, iRate and other Mac and iOS open source projects.