弱い参照のゼロ化が nil になったことをオブジェクトが認識できるようにするメカニズムはありますか?
たとえば、私はプロパティを持っています
@property (nonatomic, weak) MyClass *theObject;
オブジェクトの割り当てが解除され、プロパティが nil になったときに通知を受け取りたい。しかし、どのように?ゼロ化弱参照システムは、オブジェクトがなくなったときにセッターを使用してプロパティを nil に設定しますか?
弱い参照のゼロ化が nil になったことをオブジェクトが認識できるようにするメカニズムはありますか?
たとえば、私はプロパティを持っています
@property (nonatomic, weak) MyClass *theObject;
オブジェクトの割り当てが解除され、プロパティが nil になったときに通知を受け取りたい。しかし、どのように?ゼロ化弱参照システムは、オブジェクトがなくなったときにセッターを使用してプロパティを nil に設定しますか?
ランタイムは弱い ivar _theObect を nil に設定するだけで、カスタム セッターは呼び出されません。
あなたができること(本当に通知が必要な場合):
_theObject の割り当てが解除されると、関連付けられたオブジェクトが解放され、割り当てが解除されます (他に強い参照がない場合)。したがって、その dealloc メソッドが呼び出されます。これがあなたの「お知らせ」です。
(これは電話で書いているので、必要に応じて後で詳細を記入できます。)
オブジェクトの割り当て解除に関する通知はありません。
システムは setter メソッドを使用しません (これは、KVO 通知が発生しないことを意味します)。ivar は、ゼロになる実際の弱参照です。プロパティのweak
キーワードは、単に ivar を合成するための指示であり、オブジェクトが保持されないという公開宣言です。
いつでも独自の通知を作成してdealloc
、クラスのメソッドから送信できますが、通常、そのような通知に関心を持つべきではなく、通知が存在しない正当な理由が少なくとも 1 つあることに注意してください。
何らかの種類の自動メモリ管理が使用されている場合は常に、(定義により) オブジェクトが必要なときに正確に死ぬことを期待することはできません。これは、Objective-C の参照カウントに適用されます。どのコンポーネントも、オブジェクトの有効期間を未知の期間にわたって予期せず延長する可能性があるため、dealloc
必要なときに正確に呼び出されるという仮定にプログラムの動作を依存することは、設計が不適切であり、問題の原因となります。dealloc
クリーンアップのみに使用してください。
この経験則を試してみてください: がdealloc
まったく呼び出されなくても、プログラムは正しく動作しますか? そうでない場合は、dealloc 通知を送信するのではなく、プログラムのロジックを再考する必要があります。
いわゆる弱い参照レジストリを使用してこれを実装しました。iOS 用のオープン ソース BMCommons フレームワークの一部であるクラスBMWeakReferenceRegistryを参照してください。
このクラスは、コンテキスト オブジェクトを目的のオブジェクトに関連付けます。このオブジェクトが解放されると、コンテキスト オブジェクトも解放され、クリーンアップ ブロックが呼び出されます。
API を参照してください。
/**
* Registry for monitoring the deallocation of objects of interest to perform cleanup logic once they are released.
*/
@interface BMWeakReferenceRegistry : BMCoreObject
BM_DECLARE_DEFAULT_SINGLETON
/**
* Cleanup block definition
*/
typedef void(^BMWeakReferenceCleanupBlock)(void);
/**
* Registers a reference for monitoring with the supplied cleanup block.
* The cleanup block gets called once the reference object gets deallocated.
*
* It is possible to register the same reference multiple times with different cleanup blocks (even if owner is the same).
* If this is not intended behavior, check hasRegisteredReference:forOwner: before calling this method.
*
* @param reference The object to monitor
* @param owner An optional owner (may be specified to selectively deregister references)
* @param cleanup The cleanup block
*/
- (void)registerReference:(id)reference forOwner:(id)owner withCleanupBlock:(BMWeakReferenceCleanupBlock)cleanup;
/**
* Deregisters the specified reference for monitoring. If owner is not nil, only the monitor(s) for the specified owner is/are removed.
*
* @param reference The monitored reference
* @param owner The optional owner of the reference
*/
- (void)deregisterReference:(id)reference forOwner:(id)owner;
/**
* Checks whether a monitor already exists for the specified reference/owner. If the owner parameter is nil all owners are checked.
*
* @param reference The monitored reference
* @param owner The optional owner
* @return True if registered, false otherwise.
*/
- (BOOL)hasRegisteredReference:(id)reference forOwner:(id)owner;
@end
Martin Rの回答に基づいて、次のスニペットを思いつきました。onDeinit クロージャーで保持サイクルを作成しないようにしてください。
private var key: UInt8 = 0
class WeakWatcher {
private var onDeinit: () -> ()
init(onDeinit: @escaping () -> ()) {
self.onDeinit = onDeinit
}
static func watch(_ obj: Any, onDeinit: @escaping () -> ()) {
watch(obj, key: &key, onDeinit: onDeinit)
}
static func watch(_ obj: Any, key: UnsafeRawPointer, onDeinit: @escaping () -> ()) {
objc_setAssociatedObject(obj, key, WeakWatcher(onDeinit: onDeinit), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
deinit {
self.onDeinit()
}
}
弱い変数を初期化するときは、次のように呼び出します。
self.weakVar = obj
WeakWatcher.watch(obj, onDeinit: { /* do something */ })
弱い変数の通知システムはありません。
以下は、デリゲートのマルチキャストを実装するために使用した例です。弱い参照オブジェクト (デリゲート) の「dealloc」を監視する方法を説明すると役立つ場合があります。
マスター DelegateRef オブジェクトがあります。その配列は、実際のデリゲートをラップするすべての delegateRef の記録を保持します。ここでの主な目的は、実際のデリゲートが解放されるときに配列によって保持されている delegateRefs への強い参照を削除することです。したがって、デリゲートを追加するときに、ローカル ウォッチ オブジェクトが作成され、デリゲートに関連付けられます。ローカル ウォッチが割り当てを解除すると、delegateRef はマスター DelegateRef の配列から削除されます。
#import <objc/runtime.h>
@interface WeakWatcher : NSObject
@property (nonatomic, weak) NSMutableArray *masterarray;
@property (nonatomic, weak) DelegateRef *delegateRef;
@end
@implementation WeakWatcher
-(void)dealloc
{ // when the object dealloc, this will be called
if(_delegateRef != nil)
{
if([self.masterarray containsObject:_delegateRef])
{
[_masterarray removeObject:_delegateRef];
}
}
}
@end
@interface DelegateRef()
@end
@implementation DelegateRef
static char assoKey[] = "assoKey";
- (NSMutableArray *)array {
if (_array == nil) {
_array = [NSMutableArray array];
}
return _array;
}
-(void)addWeakRef:(id)ref
{
if (ref == nil) {
return;
}
DelegateRef *delRef = [DelegateRef new];
WeakWatcher* watcher = [WeakWatcher new]; // create local variable
watcher.delegateRef = delRef;
watcher.masterarray = self.array;
[delRef setDelegateWeakReference:ref];
objc_setAssociatedObject(ref, assoKey, watcher, OBJC_ASSOCIATION_RETAIN);
[self.array addObject:delRef];
}
@end