25

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

4

6 回答 6

33

正如超奇迹所证明的那样,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];
于 2014-11-20T08:49:05.717 回答
12

另一种类似于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);
}
于 2016-11-23T18:21:21.820 回答
3

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

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

于 2013-05-15T18:57:45.183 回答
1

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
    }

}
于 2019-10-06T22:08:04.633 回答
0

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

一般来说,nil-dealloc. 如果一个对象被解除分配,它的 iVar 是否被清零并不重要,因为对解除分配的对象或其 iVar 的任何进一步访问本身就是一个编程错误。事实上,我听到一些人争辩说最好不要在 期间清除引用-dealloc,因为它会使错误的访问更加明显/更快地暴露错误。

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

于 2013-05-15T16:43:15.163 回答
0

如果人们仍然需要解决此问题的方法,这里有一个 Swift 中的解决方案,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)
}
于 2021-12-17T15:55:41.680 回答