我知道OBJC_ASSOCIATION_ASSIGN存在,但如果目标对象被dealloced,它会将引用清零吗?还是像过去一样,需要将该引用赋值为nil,否则我们将面临错误访问的风险?
如超级神奇所示,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];
-[NSValue nonretainedObjectValue]
不返回 weak 对象,因此不执行零弱引用。 - 0xced还有一种类似于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);
}
_Nullable
。 - Kentzo尝试后,答案是否定的。
我在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");
public class WeakObjectContainer<T: AnyObject>: NSObject {
private weak var _object: T?
public var object: T? {
return _object
}
public init(with object: T?) {
_object = object
}
}
如果人们仍然需要解决这个问题,这里有一个用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)
}