我知道 OBJC_ASSOCIATION_ASSIGN 存在,但是如果目标对象被释放,它是否将引用归零?还是像过去一样,该引用需要被归零,或者我们以后冒着访问错误的风险?
6 回答
正如超奇迹所证明的那样,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
:
- (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/属性创建一个简单的传递类,并以这种方式实现相同的效果。有点笨拙,但它会工作。
如果人们仍然需要解决此问题的方法,这里有一个 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)
}