5

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.

Community
  • 1
  • 1
Anonymous White
  • 2,149
  • 3
  • 20
  • 27
  • 1
    Hate to sound mean, but the answer you are looking for is already explained in that SO post and the link it references to the apple docs. – iska Oct 28 '12 at 19:48
  • @iska - Not really. The big question the OP is asking is how it's implemented, which isn't mentioned in either the linked question or the Apple documentation. Both of those links mention the "hows" and "whys" of usage, but not the actual implementation. – pasawaya Oct 28 '12 at 20:06
  • 1
    @qegal - You are absolutely right. I read till the "What is it?" part and commented at once, forgetting the title and all the rest. Silly me. Sorry if I seemed offensive or anything :) – iska Oct 29 '12 at 18:08

2 Answers2

6

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/

Quuxplusone
  • 23,928
  • 8
  • 94
  • 159
  • 1
    Wonderful. So one way to implement it is to add a static dictionary object that pair each UIButton with the corresponding property and then look that up. I see. If I want to be anal I would ask why using NSMutableDictionary will leak because we can always set notification when an object is about to be destroyed and remove that entry but I think I got the picture. Apple engineers must have figured that out. – Anonymous White Oct 29 '12 at 04:48
  • And I suppose that static directory is somewhere deep in apple code? Would be glad to have more explanation there. – Anonymous White Oct 29 '12 at 04:49
  • "we can always set notification when an object is about to be destroyed" — I bet you're right, actually. I don't know much about NSNotificationCenter, KVO, and all the other ways Cocoa lets you hook into its internals. It's possible that you could use notifications to solve the problems that induced me to drop into C++. – Quuxplusone Oct 29 '12 at 20:26
  • Yes, the idea is that "somewhere deep in Apple code" there is the equivalent of `static vector> myAR`. Apple doesn't expose that dictionary object to you directly, but Apple does expose the accessor functions `objc_setAssociatedObject` and `objc_getAssociatedObject`, which allow you to store and retrieve whatever you like in the dictionary. I'm willing to explain more, but I don't know what else there is to be explained. – Quuxplusone Oct 29 '12 at 20:32
  • @HaryantoCiu Ironically, I just found a way to run arbitrary code when an object is deallocated... but it relies on Associative References! :) http://blog.slaunchaman.com/2011/04/11/fun-with-the-objective-c-runtime-run-code-at-deallocation-of-any-object/ – Quuxplusone Oct 29 '12 at 20:35
  • Better change `NSObject *` to `id` or at most `id `. –  Oct 29 '12 at 20:36
  • I was just copying the method prototypes from the other guy's answer [here](http://stackoverflow.com/questions/5500327/subclass-uibutton-to-add-a-property), which used `NSObject*`. But I agree that `id` is better. – Quuxplusone Oct 29 '12 at 20:39
  • 1
    @HaryantoCiu It's worth noting that you can download [the Objective-C runtime source code](http://opensource.apple.com/). It's the `objc` project under any of the Mac OS X releases. The implementation of associated objects is in `objc-references.mm`. Take a look at the `_object_set_associative_reference` and `_object_remove_assocations` functions. The second function is what `-[NSObject dealloc]` ends up calling, and yes, it's spelled incorrectly in the source code. – rob mayoff Oct 29 '12 at 20:53
  • @HaryantoCiu Some of Apple's low-level code is open-source, including the Objective-C runtime. – rob mayoff Oct 31 '12 at 06:36
-1

A sample code available on git! Click here!

thatzprem
  • 4,697
  • 1
  • 33
  • 41