弱参照をゼロにするには、OSX10.7またはiOS5が必要です。
弱い変数は、コード、ivar、またはブロックでのみ定義できます。AFAIKは、ARCがコンパイル時に有効になるため、動的に(実行時に)弱い変数を作成する方法はありません。コードを実行すると、保持とリリースがすでに追加されています。
おそらくブロックを悪用してこのような効果を達成できると言っています。
単に参照を返すブロックがあります。
__weak id weakref = strongref;
[weakrefArray addObject:[^{ return weakref; } copy]];
ブロックをヒープにコピーするには、ブロックをコピーする必要があることに注意してください。
これで、いつでも配列をウォークできます。ブロック内の割り当て解除されたオブジェクトはnilを返します。その後、それらを削除できます。
弱い参照がゼロにされたときにコードを自動的に実行させることはできません。これが必要な場合は、関連するオブジェクトの機能を利用できます。それらは、関連付けられているオブジェクトと同時に割り当てが解除されます。したがって、オブジェクトの消滅について弱いコレクションに通知する独自の歩哨タグを持つことができます。
(関連付けが唯一の参照である場合)deallocを監視する1つの関連付けられたオブジェクトがあり、関連付けられたオブジェクトにはコレクション監視へのポインターがあります。次に、歩哨の取引所で、弱いコレクションを呼び出して、監視対象のオブジェクトがなくなったことを通知します。
関連するオブジェクトに関する私の記事は次のとおりです。http://www.cocoanetics.com/2012/06/located-objects/
これが私の実装です:
---- DTWeakCollection.h
@interface DTWeakCollection : NSObject
- (void)checkInObject:(id)object;
- (NSSet *)allObjects;
@end
---- DTWeakCollection.m
#import "DTWeakCollection.h"
#import "DTWeakCollectionSentry.h"
#import <objc/runtime.h>
static char DTWeakCollectionSentryKey;
@implementation DTWeakCollection
{
NSMutableSet *_entries;
}
- (id)init
{
self = [super init];
if (self)
{
_entries = [NSMutableSet set];
}
return self;
}
- (void)checkInObject:(id)object
{
NSUInteger hash = (NSUInteger)object;
// make weak reference
NSNumber *value = [NSNumber numberWithUnsignedInteger:hash];
[_entries addObject:value];
// make sentry
DTWeakCollectionSentry *sentry = [[DTWeakCollectionSentry alloc] initWithWeakCollection:self forObjectWithHash:hash];
objc_setAssociatedObject(object, &DTWeakCollectionSentryKey, sentry, OBJC_ASSOCIATION_RETAIN);
}
- (void)checkOutObjectWithHash:(NSUInteger)hash
{
NSNumber *value = [NSNumber numberWithUnsignedInteger:hash];
[_entries removeObject:value];
}
- (NSSet *)allObjects
{
NSMutableSet *tmpSet = [NSMutableSet set];
for (NSNumber *oneHash in _entries)
{
// hash is actually a pointer to the object
id object = (__bridge id)(void *)[oneHash unsignedIntegerValue];
[tmpSet addObject:object];
}
return [tmpSet copy];
}
@end
---- DTWeakCollectionSentry.h
#import <Foundation/Foundation.h>
@class DTWeakCollection;
@interface DTWeakCollectionSentry : NSObject
- (id)initWithWeakCollection:(DTWeakCollection *)weakCollection forObjectWithHash:(NSUInteger)hash;
@end
--- DTWeakCollectionSentry.m
#import "DTWeakCollectionSentry.h"
#import "DTWeakCollection.h"
@interface DTWeakCollection (private)
- (void)checkOutObjectWithHash:(NSUInteger)hash;
@end
@implementation DTWeakCollectionSentry
{
__weak DTWeakCollection *_weakCollection;
NSUInteger _hash;
}
- (id)initWithWeakCollection:(DTWeakCollection *)weakCollection forObjectWithHash:(NSUInteger)hash
{
self = [super init];
if (self)
{
_weakCollection = weakCollection;
_hash = hash;
}
return self;
}
- (void)dealloc
{
[_weakCollection checkOutObjectWithHash:_hash];
}
@end
これは次のように使用されます。
NSString *string = @"bla";
@autoreleasepool {
_weakCollection = [[DTWeakCollection alloc] init];
[_weakCollection checkInObject:string];
__object = [NSNumber numberWithInteger:1123333];
[_weakCollection checkInObject:__object];
}
自動解放プールブロック内にallObjectsを出力すると、そこに2つのオブジェクトがあります。外にはひもしかありません。
エントリのdeallocで、オブジェクト参照がすでにnilであるため、__weakを使用できないことがわかりました。代わりに、オブジェクトのメモリアドレスをハッシュとして使用しています。これらがまだ_entriesにある間は、それらを実際のオブジェクトとして扱うことができ、allObjectsは強力な参照の自動解放された配列を返します。
注:これはスレッドセーフではありません。内部の_entriesセットへのアクセスと変更を同期するように注意する必要がある非メインキュー/スレッド上のdeallocを処理してください。
注2:2回目のチェックインで関連する歩哨が上書きされるため、これは現在、単一の弱いコレクションにチェックインするオブジェクトでのみ機能します。複数の弱いコレクションでこれが必要な場合は、代わりに歩哨にそれらのコレクションの配列が必要です。
注3:保持サイクルを回避するために、コレクションへの歩哨の参照も弱いに変更しました。
注4:ブロック構文を処理するtypedef関数とヘルパー関数は次のとおりです。
typedef id (^WeakReference)(void);
WeakReference MakeWeakReference (id object) {
__weak id weakref = object;
return [^{ return weakref; } copy];
}
id WeakReferenceNonretainedObjectValue (WeakReference ref) {
if (ref == nil)
return nil;
else
return ref ();
}