繁体   English   中英

在弱引用中使用 objc_setAssociatedObject

[英]Using objc_setAssociatedObject with weak references

我知道 OBJC_ASSOCIATION_ASSIGN 存在,但是如果目标对象被释放,它是否会将引用归零? 还是像过去那样引用需要被清零,或者我们以后可能会冒着错误访问的风险?

正如超奇迹所证明的那样, 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];

WeakObjectContainer一种类似于WeakObjectContainer选项:

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

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

尝试之后,答案是否定的。

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

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 会导致尝试访问已经被释放的对象。

0xced 的 Swift 版本答案(带类型支持):

public class WeakObjectContainer<T: AnyObject>: NSObject {

    private weak var _object: T?

    public var object: T? {
        return _object
    }

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

}

尽我所知,此行为并未在文档或标题中指定,因此它可能是您不应该依赖的实现细节,即使您能够辨别当前行为是什么。 我猜它没有被清零。 原因如下:

在一般情况下,没有必要到nil期间出实例变量的引用-dealloc 如果一个对象被释放,它的 iVar 是否被清零应该无关紧要,因为对释放的对象或其 iVar 的任何进一步访问本身就是一个编程错误。 事实上,我听过一些争论,在-dealloc期间最好不要清除引用,因为它会使错误访问明显/更快地暴露错误。

编辑:哦,我想我误读了你的问题。 你想要“归零弱引用”。 关联存储似乎不支持这些。 您可以使用一个标记为 __weak 的 ivar/property 制作一个简单的传递类,并以这种方式实现相同的效果。 有点笨拙,但它会起作用。

如果人们仍然需要解决这个问题,这里有一个 Swift 中的解决方案,用自定义的objc_getAssociatedObject替换普通的objc_getAssociatedObject来处理弱对象:

func objc_getAssociatedWeakObject(_ object: AnyObject, _ key: UnsafeRawPointer) -> AnyObject? {
    let block: (() -> AnyObject?)? = objc_getAssociatedObject(object, key) as? (() -> AnyObject?)
    return block != nil ? block?() : nil
}

func objc_setAssociatedWeakObject(_ object: AnyObject, _ key: UnsafeRawPointer, _ value: AnyObject?) {
    weak var weakValue = value
    let block: (() -> AnyObject?)? = {
        return weakValue
    }
    objc_setAssociatedObject(object, key, block, .OBJC_ASSOCIATION_COPY)
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM