Objective c 弧下零化弱参考的集合
如何在ARC下获得一个归零弱引用数组?我不希望数组保留对象。我希望数组元素在释放时删除它们自己,或者将这些条目设置为nil 同样地,我怎样才能用字典做到这一点?我不希望字典保留这些值。同样,我希望dictionary元素在释放值时删除它们自己,或者将值设置为nil。(我需要保留作为唯一标识符的键,至少在释放相应的值之前是这样。) 这两个问题涉及相似的理由:Objective c 弧下零化弱参考的集合,objective-c,cocoa-touch,cocoa,automatic-ref-counting,weak-references,Objective C,Cocoa Touch,Cocoa,Automatic Ref Counting,Weak References,如何在ARC下获得一个归零弱引用数组?我不希望数组保留对象。我希望数组元素在释放时删除它们自己,或者将这些条目设置为nil 同样地,我怎样才能用字典做到这一点?我不希望字典保留这些值。同样,我希望dictionary元素在释放值时删除它们自己,或者将值设置为nil。(我需要保留作为唯一标识符的键,至少在释放相应的值之前是这样。) 这两个问题涉及相似的理由: 但这两种方法都没有要求对引用进行归零 根据文档,NSPointerArray和NSHashMap都不支持ARC下的弱引用。NSVal
(弱)
属性创建自己的类似NSValue的包装类,如下所示。有没有更好的办法让我看不见
我正在为OS X 10.7和iOS 6.0开发。将弱引用归零需要OS X 10.7或iOS 5 只能在代码、IVAR或块中定义弱变量。AFAIK无法动态(在运行时)创建弱变量,因为ARC在编译时生效。当您运行代码时,它已经为您添加了保留和释放 已经说过,您可能会滥用块来实现这样的效果 有一个只返回引用的块
__weak id weakref = strongref;
[weakrefArray addObject:[^{ return weakref; } copy]];
请注意,您需要复制块以将其复制到堆中
现在,您可以随时遍历数组,块中解除锁定的对象将返回nil。然后您可以删除这些
当弱引用为零时,不能自动执行代码。如果这是您想要的,那么您可以利用关联对象的功能。这些对象与它们关联的对象同时被解除分配。所以你可以有你自己的哨兵标签,通知弱小的收集对象死亡
您将有一个关联的对象来监视dealloc(如果关联是唯一的引用),并且关联的对象将有一个指向监视集合的指针。然后在sentry dealloc中调用弱集合,通知它监视的对象已消失
以下是我对关联对象的写入:
以下是我的实现:
---- 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
如果在自动释放池块内输出AllObject,则其中有两个对象。外面你只有绳子
我发现在条目的dealloc中,对象引用已经是nil,因此不能使用uu弱。相反,我使用对象的内存地址作为散列。当这些仍然在_条目中时,您可以将它们视为实际对象,AllObject返回一个自动释放的强引用数组
注意:这不是线程安全的。要处理非主队列/线程上的dealloc,您需要小心同步访问和修改内部_项集
注意2:这目前仅适用于签入单个弱集合的对象,因为第二次签入将覆盖关联的sentry。如果您需要多个弱集合,那么哨兵应该拥有这些集合的数组
注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 ();
}
输出:
2013-01-08 10:00:19.171 X[6515:c07] deallocing
2013-01-08 10:00:19.172 X[6515:c07] 0x0
编辑:我根据这个想法从头开始创建了一个。这是可行的,但它的丑陋有几个原因:
我使用了一个添加@properties的键、下一个映射桶和对拥有该对象的集合的引用
一旦将对象置零,它将从集合中消失
但是,要使地图具有动态容量,它需要接收更新的元素数量,以计算负载系数,并在需要时扩展容量。也就是说,除非您希望在每次添加元素时执行迭代整个数组的Θ(n)更新。我是通过回调我要添加到集合中的示例对象的dealloc方法来实现这一点的。我可以编辑原始对象(为了简洁起见我这样做了),或者从超类继承,或者旋转dealloc。无论如何,丑陋
但是,如果您不介意使用固定容量的集合,则不需要回调。集合使用并假设散列函数的分布均匀,性能将为Θ(1+n/m),即n=元素,m=容量。但是(更多但是)为了避免破坏链接,您需要添加一个前一个链接作为category@property,并将其链接到元素dealloc中的下一个元素。一旦我们接触到dealloc,就可以通知集合元素正在被删除(这就是现在正在做的事情)
最后,请注意,项目中的测试是最小的,我可能忽略了一些东西。下面是一个零化弱引用包装类的代码。它可以与NSArray、NSSet和NSDictionary一起正常工作 此解决方案的优点是它与较旧的操作系统兼容,而且非常简单。缺点是迭代时,在使用它之前,可能需要验证
-nonretainedObjectValue
是否为非nil
这与协同学答案第一部分中的包装器的想法相同,它使用块来完成相同的事情
WeakReference.h
@interface WeakReference : NSObject {
__weak id nonretainedObjectValue;
__unsafe_unretained id originalObjectValue;
}
+ (WeakReference *) weakReferenceWithObject:(id) object;
- (id) nonretainedObjectValue;
- (void *) originalObjectValue;
@end
WeakReference.m
@implementation WeakReference
- (id) initWithObject:(id) object {
if (self = [super init]) {
nonretainedObjectValue = originalObjectValue = object;
}
return self;
}
+ (WeakReference *) weakReferenceWithObject:(id) object {
return [[self alloc] initWithObject:object];
}
- (id) nonretainedObjectValue { return nonretainedObjectValue; }
- (void *) originalObjectValue { return (__bridge void *) originalObjectValue; }
// To work appropriately with NSSet
- (BOOL) isEqual:(WeakReference *) object {
if (![object isKindOfClass:[WeakReference class]]) return NO;
return object.originalObjectValue == self.originalObjectValue;
}
@end
我只是创建了NSMutableDictionary和NSMutableSet的非线程安全弱引用版本。代码如下: 对于NSMutableArray,事情更加复杂,因为它不能包含
nil
,并且一个对象可能会多次添加到数组中。但这是可行的
@interface WeakReference : NSObject {
__weak id nonretainedObjectValue;
__unsafe_unretained id originalObjectValue;
}
+ (WeakReference *) weakReferenceWithObject:(id) object;
- (id) nonretainedObjectValue;
- (void *) originalObjectValue;
@end
@implementation WeakReference
- (id) initWithObject:(id) object {
if (self = [super init]) {
nonretainedObjectValue = originalObjectValue = object;
}
return self;
}
+ (WeakReference *) weakReferenceWithObject:(id) object {
return [[self alloc] initWithObject:object];
}
- (id) nonretainedObjectValue { return nonretainedObjectValue; }
- (void *) originalObjectValue { return (__bridge void *) originalObjectValue; }
// To work appropriately with NSSet
- (BOOL) isEqual:(WeakReference *) object {
if (![object isKindOfClass:[WeakReference class]]) return NO;
return object.originalObjectValue == self.originalObjectValue;
}
@end
@interface WeakReferenceObj : NSObject
@property (nonatomic, weak) id weakRef;
@end
@implementation WeakReferenceObj
+ (id)weakReferenceWithObj:(id)obj{
WeakReferenceObj *weakObj = [[WeakReferenceObj alloc] init];
weakObj.weakRef = obj;
return weakObj;
}
@end
@implementation NSMutableSet(WeakReferenceObj)
- (void)removeDeallocRef{
NSMutableSet *deallocSet = nil;
for (WeakReferenceObj *weakRefObj in self) {
if (!weakRefObj.weakRef) {
if (!deallocSet) {
deallocSet = [NSMutableSet set];
}
[deallocSet addObject:weakRefObj];
}
}
if (deallocSet) {
[self minusSet:deallocSet];
}
}
- (void)addWeakReference:(id)obj{
[self removeDeallocRef];
[self addObject:[WeakReferenceObj weakReferenceWithObj:obj]];
}
@end
- (void)didReceiveMemoryWarning{
[yourWeakReferenceSet removeDeallocRef];
}