簡體   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