清零弱引用需要 OS X 10.7 或 iOS 5。
您只能在代码、ivars 或块中定义弱变量。AFAIK 无法动态(在运行时)创建弱变量,因为 ARC 在编译时生效。当您运行代码时,它已经为您添加了保留和发布。
话虽如此,您可能可以滥用块来达到这样的效果。
有一个简单地返回引用的块。
__weak id weakref = strongref;
[weakrefArray addObject:[^{ return weakref; } copy]];
请注意,您需要复制块才能将其复制到堆中。
现在你可以随时遍历数组,块中的dealloc'ed对象将返回nil。然后,您可以删除这些。
当弱引用归零时,您不能自动执行代码。如果这是您想要的,那么您可以利用关联对象的功能。那些与它们关联的对象同时被释放。因此,您可以拥有自己的哨兵标签,通知弱集合有关对象消亡的信息。
您将有一个关联对象来监视 dealloc(如果关联是唯一的引用),并且关联对象将具有指向集合监视的指针。然后在哨兵dealloc中调用弱集合来通知它被监视的对象已经消失。
这是我关于关联对象的文章:http: //www.cocoanetics.com/2012/06/associated-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,那么您将在其中有两个对象。外面你只有字符串。
我发现在entry的dealloc中对象引用已经是nil了,所以不能使用__weak。相反,我使用对象的内存地址作为哈希。虽然这些仍在 _entries 中,但您可以将它们视为实际对象,并且 allObjects 返回一个自动释放的强引用数组。
注意:这不是线程安全的。处理非主队列/线程上的 dealloc,您需要小心同步访问和变异内部 _entries 集。
注意 2:这目前仅适用于检入单个弱集合的对象,因为第二次检入会覆盖关联的哨兵。如果你需要多个弱集合,那么哨兵应该有一个这些集合的数组。
注意 3:我也将哨兵对集合的引用更改为弱,以避免保留循环。
注意 4:这里有一个 typedef 和 helper 函数,它们为您处理块语法:
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 ();
}