Cuckoo for Cocoa Dev

A blog for all those Cuckoo for Cocoa Development

Adding Properties to a Class Using a Category

One of the many challenges developers face besides lack of sleep (especially true for me when I walk away from my code when I’m in the middle of a problem) is when frameworks and libraries are thrown over the wall for us to consume that are a little lacking in features. While some of us have the ability to ask for additions if the frameworks are in house, some of us do not (UIKit I’m looking at you). While it’s pretty straightforward to add a utility method to a class, it’s a little bit more involved to add a property.

Well, before I explain this in depth, I would like to post an example right away. The code itself uses some Objective-c runtime magic to work, and the average iOS developer doesn’t necessarily need to know every small detail. Feel free to HAZ TEH CODEZ…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// NSString category
@interface NSString (Cuckoo)

@property (strong, nonatomic) NSString *cuckooString;
@property (nonatomic) BOOL isFancyString;
@property (nonatomic) NSInteger favoriteNumber;

@end


#import <objc/runtime.h>
#import "NSString+Cuckoo.h"

static void *CuckooStringKey = @"CuckooStringKey";
static void *IsFancyStringKey = @"IsFancyStringKey";
static void *FavoriteNumberKey = @"FavoriteNumberKey";

@implementation NSString (Cuckoo)

- (NSString *)cuckooString {
    return objc_getAssociatedObject(self, CuckooStringKey);
}

- (void)setCuckooString:(NSString *)cuckooString {
    objc_setAssociatedObject(self, CuckooStringKey, cuckooString, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)isFancyString
{
    NSNumber *isFancyStringWrapper = objc_getAssociatedObject(self, IsFancyStringKey);
    return [isFancyStringWrapper boolValue];
}

- (void)setIsFancyString:(BOOL)isFancyString
{
    NSNumber *isFancyStringWrapper = [NSNumber numberWithBool:isFancyString];
    objc_setAssociatedObject(self, IsFancyStringKey, isFancyStringWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSInteger)favoriteNumber
{
    NSNumber *favoriteNumberWrapper = objc_getAssociatedObject(self, FavoriteNumberKey);
    return [favoriteNumberWrapper integerValue];
}

- (void)setFavoriteNumber:(NSInteger)favoriteNumber
{
    NSNumber *favoriteNumberWrapper = [NSNumber numberWithInteger:favoriteNumber];
    objc_setAssociatedObject(self, FavoriteNumberKey, favoriteNumberWrapper, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

It looks like a lot of information but for the sake of this post I wanted to include a couple of primitive property data types (BOOL and NSInteger) in addition to an object data type property. I’ve created a category for NSString called “Cuckoo” that contains three simple properties. The interface is pretty standard, but the implementation is where we use some runtime functions. In order to store data, we need to use the objc_setAssociatedObject runtime function. This function adds a key/value store to an Objective-c object that we can use to store an additional “state” to the NSString class. Once the data has been stored, we then use the objc_getAssociatedObject function to get it back out later.

The first property, cuckooString, is the most straightforward. We first create a static key for the NSString we are going to store, and then create the getter/setter methods for cuckooString. In the getter we return the object returned from objc_getAssociatedObject using the category class itself as the target, and the value associated from the key we used for the store.

The second/third properties require a little bit more setup. Since the get/set associated object functions require an object, we need to use a wrapper when we insert it into the key/value store. For our example, I’ve used NSNumber wrapper objects for our BOOL and NSInteger properties. When we get the values, we unwrap the value from the NSNumber, and when we set the values, we wrap the value into the NSNumber.

Now that we have these additional properties, lets use them:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NSString *goat = @"Billy McGoat";

goat.cuckooString = @"Cuckoo for Cocoa Dev!!!";
goat.isFancyString = YES;
goat.favoriteNumber = 7;

NSLog(@"================> Cuckoo String: %@", goat.cuckooString);
NSLog(@"================> Cuckoo Is fancy string?: %d", goat.isFancyString);
NSLog(@"================> Cuckoo Favorite Number: %d", goat.favoriteNumber);

// Log output:
// ================> Cuckoo String: Cuckoo for Cocoa Dev!!!
// ================> Cuckoo Is fancy string?: 1
// ================> Cuckoo Favorite Number: 7

With just a little bit of Objective-c runtime magic we can now add a property to a class using a category. While it’s not as straight forward to setup as a regular add-on method, properties can be added to give closed classes additional functionality.

Comments