Skip to content

Instantly share code, notes, and snippets.

@jeffbailey
Created January 21, 2014 01:00
Show Gist options
  • Save jeffbailey/8532412 to your computer and use it in GitHub Desktop.
Save jeffbailey/8532412 to your computer and use it in GitHub Desktop.
Technique for using associated objects to add a property to a class using a category. See http://iosdevelopertips.com/objective-c/adding-properties-category-using-associated-objects.html.
@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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment