OBJC_ASSOCIATION_ASSIGN が存在することは知っていますが、ターゲット オブジェクトの割り当てが解除された場合、参照はゼロになりますか? それとも、その参照を nil-ed にする必要があった昔のようなものですか? または、後で不正なアクセスが発生するリスクがありますか?
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];
次のようなもう 1 つのオプション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);
}
試してみると、答えはNOです。
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 を使用すると、既に割り当てが解除されているオブジェクトにアクセスしようとする結果になります。
この動作は、私が知る限り、ドキュメントやヘッダーで指定されていないため、現在の動作が何であるかを識別できたとしても、当てにすべきではない実装の詳細である可能性があります。私はそれがゼロにされていないと推測します。理由は次のとおりです。
nil
一般に、実行中に iVar で参照を出力する必要はありません-dealloc
。オブジェクトの割り当てが解除された場合、その iVar がゼロになっているかどうかは問題ではありません。これは、割り当て解除されたオブジェクトまたはその iVar にそれ以上アクセスすることは、それ自体がプログラミング エラーであるためです。実際、エラー アクセスがより明白になり、バグがより早く露呈するため、実行中は参照をクリアしないほうがよいと主張する人もいます。-dealloc
編集:ああ、私はあなたの質問を読み違えたと思います。「弱い参照をゼロにする」ことが必要です。関連ストレージはそれらをサポートしていないようです。1 つの ivar/property が __weak としてマークされた簡単なパススルー クラスを作成し、その方法で同じ効果を得ることができます。少し不器用ですが、うまくいきます。
これに対する回避策が必要な場合は、objc_getAssociatedObject
弱いオブジェクトを処理するために通常のオブジェクトをカスタム オブジェクトに置き換える Swift のソリューションを次に示します。
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)
}