How is Associative Reference implemented?

associative, objective-c

Solution

With the Associative References trick, you're not actually adding any instance data to the UIButton object. Instead, you're using a totally separate Cocoa facility to create a new dictionary mapping (or associating) existing UIButton objects with data that's stored elsewhere in the heap.

You could do exactly the same thing without using Cocoa's Associative References; it would just be even uglier and probably less efficient. It would go something like this, in Objective-C++. (I'm not even going to try to write it in Objective-C, because `CFMutableDictionary` and `NSMutableDictionary` both have the wrong behavior on a couple of levels, and I'm not going to write the whole thing from scratch. However, C++'s `std::map` can't be used with `__weak` references the way I want to use it, so I'm falling back on this inefficient `std::vector` algorithm. For those unfamiliar with C++: `std::vector` is roughly equivalent to an `NSMutableArray`, except that you get to choose whether it retains its contents.)

The point is that the UIButton objects aren't being changed; what's changing is the contents of this additional dictionary. The property getter and setter simply know how to look things up in that dictionary so that it appears as if the UIButton has a new property.

#import "UIButton+Property.h"
#import <algorithm>
#import <vector>

typedef std::pair<__weak id, __strong id> EntryType;
static std::vector<EntryType> myAR;

@implementation UIButton(Property)

-(void) setProperty:(id)property
{
    for (int i=0; i < myAR.size(); ++i) {
        if (myAR[i].first == self) {
            myAR[i].second = property;
            return;
        }
    }
    myAR.push_back(EntryType(self, property));
}

-(id) property
{
    /* To save space, periodically erase the dictionary entries for
     * UIButton objects that have been deallocated. You can skip this
     * part, and even use NSMutableDictionary instead of this C++
     * stuff, if you don't care about leaking memory all over the place.
     */
    size_t n = myAR.size();
    for (size_t i=0; i < n; ++i) {
        if (myAR[i].first == nil)
            myAR[i] = myAR[--n];
    }
    myAR.resize(n);

    /* Look up "self" in our dictionary. */
    for (size_t i=0; i < myAR.size(); ++i) {
        EntryType &entry = myAR[i];
        if (entry.first == self) {
            return entry.second;
        }
    }
    return nil;
}

@end

See also: http://labs.vectorform.com/2011/07/objective-c-associated-objects/

Problem

I saw a very good sample here: Subclass UIButton to add a property What is it? You can't add object to a category. But now with this trick you can. So what is it? How does it work? Objective-c object already have some constant number of ivar pointers right? Now you add another one? How did they figure that out? A pretty ugly notation I must admit.

Original source

Related problems