76

In a project I have taken on, the original author has opted to use objc_setAssociatedObject() and I'm not 100% clear what it does or why they decided to use it.

I decided to look it up and, unfortunately, the docs aren't very descriptive about its purpose.

objc_setAssociatedObject
Sets an associated value for a given object using a given key and association policy.
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
Parameters
object
The source object for the association.
key
The key for the association.
value
The value to associate with the key key for object. Pass nil to clear an existing association.
policy
The policy for the association. For possible values, see “Associative Object Behaviors.”

So what exactly does this function do and in what cases should it be used?


Edit after reading answers

So what is the point in the following code?

Device *device = [self.list objectAtIndex:[indexPath row]];
DeviceViewController *next = [[DeviceViewController alloc] initWithController:self.controller
                                                                            device:device
                                                                               item:self.rootVC.selectedItem];  
    objc_setAssociatedObject(device, &kDeviceControllerKey, next, OBJC_ASSOCIATION_RETAIN);

What is the point in associating the device with the view controller if it's already an instance variable?

Jasarien
  • 58,279
  • 31
  • 157
  • 188

4 Answers4

71

objc_setAssociatedObject adds a key value store to each Objective-C object. It lets you store additional state for the object, not reflected in its instance variables.

It's really convenient when you want to store things belonging to an object outside of the main implementation. One of the main use cases is in categories where you cannot add instance variables. Here you use objc_setAssociatedObject to attach your additional variables to the self object.

When using the right association policy your objects will be released when the main object is deallocated.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • Thanks for the explanation. I'm doubting whether or not the code is using the function in a useful way, after reading this. I edited the question to include the code. – Jasarien May 06 '11 at 09:59
  • Can't we use a dictionary in this case keep the object ? – AppleBee Mar 23 '15 at 06:12
  • 1
    @user3127620 That's (semantically) what `objc_setAssociatedObject` does. The advantage of this mechanism is that it can do it without altering the class (i.e. without adding an ivar). So you can do it on framework classes or other cases where you don't own the source code. – Nikolai Ruhe Mar 23 '15 at 08:57
  • hi @NikolaiRuhe - you may know the full answer/explanation to this question: http://stackoverflow.com/q/41827988/294884 tricky :/ – Fattie Jan 24 '17 at 13:47
36

From the reference documents on Objective-C Runtime Reference:

You use the Objective-C runtime function objc_setAssociatedObject to make an association between one object and another. The function takes four parameters: the source object, a key, the value, and an association policy constant. 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 pattern is
    similar to that of the attributes of
    a declared property (see “Property
    Declaration Attributes”). You specify the policy for the relationship using a constant (see
    objc_AssociationPolicy and
    Associative Object Behaviors).

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.

Yuchen
  • 30,852
  • 26
  • 164
  • 234
visakh7
  • 26,380
  • 8
  • 55
  • 69
  • Hi, I was reviewing the docs with the intention of posting a similar answer. As you beat me to it, I took the liberty of making a hypertext link in your answer instead. Hope you don't mind. – JeremyP May 06 '11 at 09:41
  • @JeremyP: Sure thing. Thanks a lot for that. Missed to add the link. Thanks for your contribution – visakh7 May 06 '11 at 09:42
  • Could share a few reputations if I could. :P – visakh7 May 06 '11 at 09:43
  • Thanks for the explanation. I now have a doubt as to whether the code in which this function is used is sane... I edited the question to include the code. – Jasarien May 06 '11 at 09:59
  • If its an ivar y is that it uses Device *device = [self.list objectAtIndex:[indexPath row]]; DeviceViewController *next = [[DeviceViewController alloc].... – visakh7 May 06 '11 at 10:04
  • 1
    That's what I'm asking... Why would you do that if it's already an ivar? (I didn't write this code, honestly.) – Jasarien May 06 '11 at 10:16
  • 3
    Still not clear in which situations this would be useful/needed, versus the existing retain/release/autorelease etc... – Nicolas Miari Jul 18 '12 at 14:00
  • I provided a couple of [examples](http://stackoverflow.com/a/16313377/766570) with a bit more context – abbood May 01 '13 at 05:53
  • The apple doc can be read here. http://web.archive.org/web/20120818164935/http://developer.apple.com/library/ios/#/web/20120820002100/http://developer.apple.com/library/ios/documentation/cocoa/conceptual/objectivec/Chapters/ocAssociativeReferences.html – cnotethegr8 Jan 07 '14 at 12:11
  • Re: "The key for each association must be unique." Do you mean unique across all objects, or does the key only need to be unique in relation to the first object it's assigned to? In other words, say I'm assigning something to ButtonA with that key. If I try to get an object on ButtonB using the same key, I would assume I'd get 'nil', correct? – Mark A. Donohoe Jan 17 '20 at 06:13
29

Here is a list of use cases for object associations:

one: To add instance variables to categories. In general this technique is advised against, but here is an example of a legitimate use. Let's say you want to simulate additional instance variables for objects you cannot modify (we are talking about modifying the object itself, ie without subclassing). Let's say setting a title on a UIImage.

// UIImage-Title.h:
@interface UIImage(Title)
@property(nonatomic, copy) NSString *title;
@end 

// UIImage-Title.m:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>

static char titleKey;

@implementation UIImage(Title)
- (NSString *)title
{
    return objc_getAssociatedObject(self, &titleKey);
}

- (void)setTitle:(NSString *)title
{
    objc_setAssociatedObject(self, &titleKey, title, OBJC_ASSOCIATION_COPY);
}
@end

Also, here is a pretty complex (but awesome) way of using associated objects with categories.. it basically allows you to pass in a block instead of a selector to a UIControl.


two: Dynamically adding state information to an object not covered by its instance variables in conjunction with KVO.

The idea is that your object gains state information only during runtime (ie dynamically). So the idea is that although you can store this state info in an instance variable, the fact that you're attaching this info into a an object instantiated at runtime and dynamically associating it with the other object, you are highlighting the fact that this is a dynamic state of the object.

One excellent example that illustrates this is this library, in which associative objects are used with KVO notifications. Here is an excerpt of the code (note: this KVO notification isn't necessary to run make the code in that library work.. rather it's put there by the author for convenience, basically any object that registers to this will be notified via KVO that changes have happened to it):

static char BOOLRevealing;

- (BOOL)isRevealing
{
    return [(NSNumber*)objc_getAssociatedObject(self, &BOOLRevealing) boolValue];
} 

- (void)_setRevealing:(BOOL)revealing
{
    [self willChangeValueForKey:@"isRevealing"];
    objc_setAssociatedObject(self, &BOOLRevealing, 
       [NSNumber numberWithBool:revealing], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self didChangeValueForKey:@"isRevealing"];
}

bonus: take a look at this discussion/explanation of associated objects by Mattt Thompson, author of the seminal AFNetworking library

Community
  • 1
  • 1
abbood
  • 23,101
  • 16
  • 132
  • 246
6

To answer your revised question:

What is the point in associating the device with the view controller if it's already an instance variable?

There are several reasons why you might want to do this.

  • the Device class doesn't have a controller instance variable, or property and you can't change it or subclass it e.g. you don't have the source code.
  • you want two controllers associated with the device object and you can't change the device class or subclass it.

Personally, I think it is very rare to need to use low level Objective-C runtime functions. This looks like a code smell to me.

JeremyP
  • 84,577
  • 15
  • 123
  • 161