Associated Objects, Categories, And You!

Objective-C categories are a beautiful thing. They allow you to quickly extend a class’s functionality without creating a subclass. One major shortcoming of categories, however, is their inability to hold extra properties/variables. Kind of lame huh? Well it doesn’t have to be! By leveraging the Objective-C runtime library, we can add extra properties to our categories via magical things called associated objects.

Associated Objects… What?


Also known as associative references, associated objects allow us to add additional data to classes with minimal effort. As said above, we can even use them to add data to categories! This kicks ass. Really, it does. Trust me.

I’m Interested, How Do I Use Them?


There are only two methods you need to know in order to use associated objects:

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
id objc_getAssociatedObject(id object, const void *key);

objc_setAssociatedObject adds a value to a target object using a unique key & association policy. objc_getAssociatedObject gets an associated object stored on a target object using it’s unique key. It’s a little more difficult than simply assigning a value to a property, but the outcome is the same!

UIImage+Name.h

#import <UIKit/UIKit.h>

@interface UIImage (Name)
@property (nonatomic, copy) NSString *name;
@end

UIImage+Name.m

#import "UIImage+Name.m"
#import <objc/runtime.h>

static const void *NameKey = &NameKey;

@implementation UIImage (Name)

- (void)setName:(NSString *)name {

    objc_setAssociatedObject(self, NameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);

}

- (NSString *)name {

    objc_getAssociatedObject(self, NameKey);

}

@end

Alright, let’s break it down.

We define a custom name property in our UIImage category. Usually, matching setter and getter methods for our property would be synthesized for us, but because this is a category we will have to manually implement them. Mildly annoying, but easy – so don’t complain.

Our associated object will be stored and fetched using a unique key. It really doesn’t matter what the key is, it just needs to be persistent and unchanging. In this case, we initialize our key as a pointer to itself. This way we don’t allocate additional memory for our key, as it just points to the address of itself. It’s guaranteed to be unique (because it’s static), and will never change (because it’s const).

We implement the setter method for our property: -(void)setName:(NSString *)name by storing the passed in name argument as an associated object. the objc_AssociationPolicy determines how the object should be stored (in this case nonatomic, copy). A list of available association policies can be found in the Objective-C Runtime Reference.

Similarly, the getter for our property -(NSString *)name is implemented by returning the associated object for our key.

A Note On Unique Keys


After doing a little research, I stumbled upon this little trick that makes associated objects a little easier to work with. Instead of creating our own unique keys for our objects, we can just use selectors as our own make-shift keys. As it turns out, selectors (method names) are const pointers in-of themselves. This means that they are completely viable for use as associative reference keys! The new implementation would look a little something like this:

UIImage+Name.m

#import "UIImage+Name.m"
#import <objc/runtime.h>

@implementation UIImage (Name)

- (void)setName:(NSString *)name {

    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);

}

- (NSString *)name {

    objc_getAssociatedObject(self, @selector(name));

}

@end

Notice how we no longer need to create our own key. Yay for fewer lines of code!

Keep in mind though, selectors are always publicly accessible, if you are adding private properties, it might be a better option to just create your own static keys (these are private) if you care about keeping your private things well… private.