[英]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.