ARC下归零弱引用的集合

pau*_*kow 40 cocoa cocoa-touch weak-references objective-c automatic-ref-counting

如何在ARC下获得一系列归零弱引用?我不希望数组保留对象.我希望数组元素在被释放时自行删除,或者将这些条目设置为nil.

同样,我怎么能用字典做到这一点?我不希望字典保留值.再次,我希望字典元素要么在取消分配值时删除自己,要么将值设置为nil.(我需要保留密钥,这是唯一的标识符,至少在相应的值被释放之前.)

这两个问题涉及类似的问题:

但两者都没有要求归零.

根据文档,NSPointerArray和NSHashMap都不支持ARC下的弱引用.NSValue的nonretainedObjectValue也不起作用,因为它是非归零的.

我看到的唯一解决方案是创建我自己的类似NSValue的包装类,其中包含一个(weak)属性,正如这个答案提到的那样,接近结尾.有没有更好的方式我没有看到?

我正在为OS X 10.7和iOS 6.0开发.

Coc*_*ics 24

归零弱引用需要OS X 10.7或iOS 5.

您只能在代码,ivars或块中定义弱变量.AFAIK无法动态(在运行时)创建弱变量,因为ARC在编译期间生效.当您运行代码时,它已经为您添加了保留和版本.

说过你可能会滥用块来实现这样的效果.

有一个只返回引用的块.

__weak id weakref = strongref;
[weakrefArray addObject:[^{ return weakref; } copy]];
Run Code Online (Sandbox Code Playgroud)

请注意,您需要复制块以将其复制到堆中.

现在你可以随时随地走数组,块中的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
Run Code Online (Sandbox Code Playgroud)

这将使用这样:

NSString *string = @"bla";

@autoreleasepool {
_weakCollection = [[DTWeakCollection alloc] init];
    [_weakCollection checkInObject:string];

__object = [NSNumber numberWithInteger:1123333];

[_weakCollection checkInObject:__object];
}
Run Code Online (Sandbox Code Playgroud)

如果在自动释放池块中输出allObjects,那么你有两个对象.外面你只有字符串.

我发现在条目的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 ();
}
Run Code Online (Sandbox Code Playgroud)


pau*_*kow 17

这是一个归零弱引用包装类的代码.它适用于NSArray,NSSet和NSDictionary.

这个解决方案的优点是它与旧的操作系统兼容,而且很简单.缺点是在迭代时,您可能需要-nonretainedObjectValue在使用它之前验证它是否为非零.

它与Cocoanetics的第一部分中的包装器的想法相同,它使用块来完成同样的事情.

WeakReference.h

@interface WeakReference : NSObject {
    __weak id nonretainedObjectValue;
    __unsafe_unretained id originalObjectValue;
}

+ (WeakReference *) weakReferenceWithObject:(id) object;

- (id) nonretainedObjectValue;
- (void *) originalObjectValue;

@end
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


Tri*_*ops 8

NSMapTable应该适合你.适用于iOS 6.