关联引用是如何实现的?

5
我在这里看到了一个非常好的示例:子类化UIButton以添加属性 这是什么?你无法将对象添加到分类中。但是现在通过这个技巧,你可以实现。
那么这是什么?它是如何工作的?
Objective-C对象已经有一些常量IVAR指针对吧?
现在你又添加了一个?他们是如何想出来的呢?
这是一个相当丑陋的符号表示法,我必须承认。

1
不想听起来过于苛刻,但你所寻找的答案已经在那个SO帖子中得到了解释,并且它引用了苹果文档的链接。 - iska
@iska - 不完全是。OP所问的重点是它是如何实现的,这在链接的问题或苹果文档中都没有提到。这两个链接都提到了使用的“方法”和“原因”,但没有提到实际的实现方式。 - pasawaya
1
@qegal - 你说得一点都没错。我只看了到"这是什么?"的部分就立刻评论了,忘记了标题和所有其他内容。真是愚蠢的我。如果显得冒犯或有什么不妥之处,我很抱歉 :) - iska
2个回答

6
使用关联引用技巧时,您实际上并没有向 UIButton 对象添加任何实例数据。相反,您正在使用一个完全独立的 Cocoa 工具来创建一个新字典映射(或“关联”)现有的 UIButton 对象与存储在堆中的数据。
您可以完全不使用 Cocoa 的关联引用来做完全相同的事情;只是它会更丑陋,可能效率更低。在 Objective-C++ 中,它可能会像这样写。(我甚至不会尝试用 Objective-C 来写它,因为 CFMutableDictionary 和 NSMutableDictionary 在几个层面上都有错误的行为,而我又不想从头开始写整个东西。但是,C++ 的 std::map 无法像我想要使用的那样与 __weak 引用一起使用,因此我退回到了这种效率低下的 std::vector 算法。对于不熟悉 C++ 的人:std::vector 大致相当于一个 NSMutableArray,只是您可以选择它是否保留其内容。)
重点是 UIButton 对象没有被改变;改变的是这个额外字典的内容。属性 getter 和 setter 只是知道如何在字典中查找东西,以便它看起来好像 UIButton 有一个新属性。
#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

另请参阅:http://labs.vectorform.com/2011/07/objective-c-associated-objects/

1
太棒了。一种实现它的方法是添加一个静态字典对象,将每个UIButton与相应的属性配对,然后查找该字典。我明白了。如果我想要严谨一些,我会问为什么使用NSMutableDictionary会泄漏,因为我们总是可以在对象即将被销毁时设置通知并删除该条目,但我认为我已经明白了。苹果工程师一定已经想到了这一点。 - Anonymous White
“我们总是可以在对象即将被销毁时设置通知”——我敢打赌你说得对。实际上,我对NSNotificationCenter、KVO以及Cocoa让你钩入其内部的所有其他方式都不太了解。有可能你可以使用通知来解决导致我转向C++的问题。 - Quuxplusone
最好将“NSObject *”更改为“id”,或者至多更改为“id <NSObject>”。 - user529758
我只是从另一个人的答案这里中复制了方法原型,他使用了NSObject*。但我同意id更好。 - Quuxplusone
1
值得一提的是,您可以下载Objective-C运行时源代码。它是在任何Mac OS X版本下的objc项目中。关联对象的实现在objc-references.mm中。看看_object_set_associative_reference_object_remove_assocations函数。第二个函数就是-[NSObject dealloc]最终调用的函数,是的,在源代码中拼写错误。 - rob mayoff
显示剩余4条评论

-1

在 git 上有一个示例代码!点击这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接