Using objc_setAssociatedObject with weak references

只谈情不闲聊 提交于 2019-11-28 21:57:48

问题


I know that OBJC_ASSOCIATION_ASSIGN exists, but does it zero the reference if the target object is dealloced? Or is it like the old days where that reference needs to get nil-ed or we risk a bad access later on?


回答1:


As ultramiraculous demonstrated, OBJC_ASSOCIATION_ASSIGN does not do zeroing weak reference and you risk to access a deallocated object. But it’s quite easy to implement yourself. You just need a simple class to wrap an object with a weak reference:

@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

Then you must associate the WeakObjectContainer as OBJC_ASSOCIATION_RETAIN(_NONATOMIC):

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

and use the object property to access it in order to get a zeroing weak reference:

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



回答2:


One more option similar to 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);
}



回答3:


After trying it out, the answer is NO.

I ran the following code under the iOS 6 Simulator, but it would probably have the same behavior with previous iterations of the runtime:

NSObject *test1 = [NSObject new];

NSObject __weak *test2 = test1;

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

test1 = nil;

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

In the end, test1 and test2 are nil, and test3 is the pointer previously stored into test1. Using test3 would result in trying to access an object that had already been dealloced.




回答4:


This behavior isn't specified in the docs or headers as best I can tell, so it's likely an implementation detail that you shouldn't count on, even if you were able to discern what the current behavior is. I would guess that it is not zeroed out. Here's why:

In general, there is no need to nil out references in iVars during -dealloc. If an object is dealloced, it shouldn't matter if its iVars were zeroed out, because any further accessing of the dealloced object or its iVars is, in and of itself, a programming error. In fact, I've heard some argue that it's better to not clear out references during -dealloc, because it will make erroneous accesses more obvious/expose bugs sooner.

EDIT: Oh, I guess I misread your question. You want "zeroing weak references". Associated storage doesn't appear to support those. You could make a trivial pass-through class with one ivar/property marked as __weak and achieve the same effect that way. A little kludgey, but it'd work.




回答5:


Swift version of answer by 0xced (with type support):

public class WeakObjectContainer<T: AnyObject>: NSObject {

    private weak var _object: T?

    public var object: T? {
        return _object
    }

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

}


来源:https://stackoverflow.com/questions/16569840/using-objc-setassociatedobject-with-weak-references

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!