将objc_setAssociatedObject与弱引用一起使用

时间:2013-05-15 16:00:22

标签: objective-c weak-references objective-c-runtime

我知道OBJC_ASSOCIATION_ASSIGN存在,但如果目标对象被解除分配,它会将引用归零吗?或者它是否像旧时代的参考需要被取消或者我们以后会冒错误的访问?

5 个答案:

答案 0 :(得分:32)

正如超级神奇所证明的那样,OBJC_ASSOCIATION_ASSIGN不会将弱引用归零,并且您有可能访问已解除分配的对象。但是你自己很容易实现。你只需要一个简单的类来包装一个弱引用的对象:

@interface WeakObjectContainer : NSObject
@property (nonatomic, readonly, weak) id object;
@end

@implementation WeakObjectContainer
- (instancetype) initWithObject:(id)object
{
    if (!(self = [super init]))
        return nil;

    _object = object;

    return self;
}
@end

然后您必须将WeakObjectContainer关联为OBJC_ASSOCIATION_RETAIN(_NONATOMIC):

objc_setAssociatedObject(self, &MyKey, [[WeakObjectContainer alloc] initWithObject:object], OBJC_ASSOCIATION_RETAIN_NONATOMIC);

并使用object属性访问它以获得归零弱引用:

id object = [objc_getAssociatedObject(self, &MyKey) object];

答案 1 :(得分:5)

另一个类似于WeakObjectContainer的选项:

- (id)weakObject {
    id (^block)() = objc_getAssociatedObject(self, @selector(weakObject));
    return (block ? block() : nil);
}

- (void)setWeakObject:(id)object {
    id __weak weakObject = object;
    id (^block)() = ^{ return weakObject; };
    objc_setAssociatedObject(self, @selector(weakObject),
                             block, OBJC_ASSOCIATION_COPY);
}

答案 2 :(得分:3)

尝试后,答案是否定的。

我在iOS 6 Simulator下运行了以下代码,但它可能与之前的运行时迭代具有相同的行为:

NSObject *test1 = [NSObject new];

NSObject __weak *test2 = test1;

objc_setAssociatedObject(self, "test", test1, OBJC_ASSOCIATION_ASSIGN);

test1 = nil;

id test3 = objc_getAssociatedObject(self, "test");

最后,test1和test2为nil,test3是先前存储在test1中的指针。使用test3会导致尝试访问已经解除分配的对象。

答案 3 :(得分:0)

我可以告诉您在文档或标题中未指定此行为,因此即使您能够辨别当前行为是什么,它也可能是您不应指望的实现细节。我猜它是归零。原因如下:

通常,在nil期间,无需{i} {i}} iVars中的引用。如果一个对象被解除分配,那么它的iVars是否被清零也无关紧要,因为对dealloced对象或其iVars的任何进一步访问本身就是一个编程错误。事实上,我听到有人认为在-dealloc期间清除引用更好,因为它会使错误的访问更多明显/更快地暴露错误

编辑:哦,我想我误解了你的问题。你想要“归零弱引用”。关联存储似乎不支持这些。你可以创建一个简单的传递类,其中一个ivar /属性标记为__weak,并达到相同的效果。有点kludgey,但它的工作。

答案 4 :(得分:0)

通过0xced(具有类型支持)的答案的快速版本:

public class WeakObjectContainer<T: AnyObject>: NSObject {

    private weak var _object: T?

    public var object: T? {
        return _object
    }

    public init(with object: T?) {
        _object = object
    }

}