You use associative references to simulate the addition of object instance variables to an existing class.
Associative references are only available in Mac OS X v10.6 and later.
Using associative references, you can add storage to an object without modifying the class declaration. This may be useful if you do not have access to the source code for the class, or if for binary-compatibility reasons you cannot alter the layout of the object.
Associations are based on a key, so for any object you can add as many associations as you want, each using a different key. An association can also ensure that the associated object remains valid for at least the lifetime of the source object (without the possibility of introducing uncollectible cycles in a garbage-collected environment).
You use the Objective-C runtime function objc_setAssociatedObject
to make an association between one object and another. The function takes four arguments: the source object, a key, the value, and an association policy constant. Of these, the key and the association policy merit further discussion.
The key is a void
pointer. The key for each association must be unique. A typical pattern is to use a static
variable.
The policy specifies whether the associated object is assigned, retained, or copied, and whether the association is be made atomically or non-atomically. This follows a similar pattern to the attributes of a declared property (see “Property Declaration Attributes”). You specify the policy for the relationship using a constant (see objc_AssociationPolicy
).
The following example shows how you can establish an association between an array and a string.
Listing 7-1 Establishing an association between an array and a string
static char overviewKey; |
NSArray *array = [[NSArray alloc] initWithObjects:@"One", @"Two", @"Three", nil]; |
// For the purposes of illustration, use initWithFormat: to ensure the string can be deallocated |
NSString *overview = [[NSString alloc] initWithFormat:@"%@", @"First three numbers"]; |
objc_setAssociatedObject(array, &overviewKey, overview, OBJC_ASSOCIATION_RETAIN); |
[overview release]; |
// (1) overview valid |
[array release]; |
// (2) overview invalid |
At point (1), the string overview
is still valid because the OBJC_ASSOCIATION_RETAIN
policy specifies that the array retains the associated object. When the array is deallocated, however (at point 2), overview
is released and so in this case also deallocated. If you try to, for example, log the value of overview
, you generate a runtime exception.
You retrieve an associated object using the Objective-C runtime function objc_getAssociatedObject
. Continuing the example shown in Listing 7-1, you could retrieve the overview from the array using the following line of code:
NSString *associatedObject = (NSString *)objc_getAssociatedObject(array, &overviewKey); |
To break an association, you typically use objc_setAssociatedObject
, passing nil
as the value.
Continuing the example shown in Listing 7-1, you could break the association between the array and the string overview
using the following line of code:
objc_setAssociatedObject(array, &overviewKey, nil, OBJC_ASSOCIATION_ASSIGN); |
(Given that the associated object is being set to nil
, the policy isn’t actually important.)
To break all associations for an object, you can use objc_removeAssociatedObjects
. In general, however, you are discouraged from using this since this breaks all associations for all clients. You only use this function if you need to restore an object to “pristine condition.”
The following program combines the code samples from the preceding sections.
#import <Foundation/Foundation.h> |
#import <objc/runtime.h> |
int main (int argc, const char * argv[]) { |
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; |
static char overviewKey; |
NSArray *array = [[NSArray alloc] initWithObjects:@"One", @"Two", @"Three", nil]; |
// For the purposes of illustration, use initWithFormat: to ensure we get a |
// deallocatable string |
NSString *overview = [[NSString alloc] initWithFormat:@"%@", @"First three numbers"]; |
objc_setAssociatedObject(array, &overviewKey, overview, OBJC_ASSOCIATION_RETAIN); |
[overview release]; |
NSString *associatedObject = (NSString *)objc_getAssociatedObject(array, &overviewKey); |
NSLog(@"associatedObject: %@", associatedObject); |
objc_setAssociatedObject(array, &overviewKey, nil, OBJC_ASSOCIATION_ASSIGN); |
[array release]; |
[pool drain]; |
return 0; |
} |
Last updated: 2010-07-13