归零下ARC弱引用的集合(Collections of zeroing weak reference

2019-07-17 23:46发布

我怎样才能获得ARC下归零弱引用数组? 我不想阵列保留的对象。 我想,当他们释放,或设置这些条目零数组元素要么自行拆除。

同样的,我怎么能做到这一点有字典吗? 我不希望字典保留值。 再次,我想的字典元素或者删除自己当值被释放,或者设置的值为零。 (I需要保留键,它们是唯一的识别符,至少直到相应的值被释放。)

这两个问题涉及类似地面:

  • 在ARC对象弱引用的NSArray
  • 具有无保留ID对象的列表?

但无论是要求零引用

每文档,既不NSPointerArray也不NSHashMap支持ARC下弱引用。 NSValue的nonretainedObjectValue不会工作,因为它是不归零。

我看到的唯一的解决方案是创建自己的 NSValue类包装类与(weak)属性, 这个答案提到,接近尾声 。 有没有更好的办法,我没有看到?

我开发OS X 10.7和iOS 6.0。

Answer 1:

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

你只可以在代码中,实例变量或块定义弱变量。 据我所知是没有办法动态地(在运行时)创建一个弱的变量,因为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那么你在那里有两个对象。 外面你只有字符串。

我发现,在入境的dealloc对象引用已经是零,因此不能使用__weak。 相反,我使用的对象作为哈希的内存地址。 虽然这些仍然在_entries你可以把它们当作实际对象和allObjects返回强引用的一个autoreleased数组。

注意:这不是线程安全的。 做处理非主队列的dealloc的/你会需要线程要小心同步访问和突变设置内部_entries。

注2:本目前仅适用于对象检查到一个单一的集合弱,因为第二个检查将覆盖相关的哨兵。 如果你需要这个多弱集合然后放哨,而不是应该有这些集合的数组。

注3:我改变了哨兵的参考收集到弱以及避免保留周期。

注4:这是一些处理块语法为你的类型定义和辅助函数:

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 ();
}


Answer 2:

下面是一个归零弱引用包装类代码。 它具有的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

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


Answer 3:

NSMapTable应该为你工作。 可用在IOS 6。



Answer 4:

@interface Car : NSObject
@end
@implementation Car
-(void) dealloc {
    NSLog(@"deallocing");
}
@end


int main(int argc, char *argv[])
{
    @autoreleasepool {
        Car *car = [Car new];

        NSUInteger capacity = 10;
        id __weak *_objs = (id __weak *)calloc(capacity,sizeof(*_objs));
        _objs[0] = car;
        car = nil;

        NSLog(@"%p",_objs[0]);
        return EXIT_SUCCESS;
    }
}

输出:

2013-01-08 10:00:19.171 X[6515:c07] deallocing
2013-01-08 10:00:19.172 X[6515:c07] 0x0

编辑 :我创建了一个样本弱地图收集从头基于这一想法。 它的工作原理,但它的丑陋以下几个原因:

我用了一个关于NSObject的类别添加@properties为重点,下图桶,并收集拥有该对象的引用。

一旦你无对象从集合中消失。

但是,对于地图以具有动态容量,它需要接收更新的元素数量,以计算负载因子,并根据需要扩大容量。 也就是说,除非你想执行Θ(n)的更新每次添加一个元素遍历整个阵列。 我这样做对我加入到集合中的采样对象的dealloc方法的回调。 我可以编辑原始对象(我做了简洁),或者从超类继承,或调酒的的dealloc。 在任何情况下,难看。

但是,如果你不介意有一个固定容量集合,你不需要回调。 该集合使用分离链和假定哈希函数的均匀分布,性能会Θ(1 + N / M)为n =元素,M =容量。 但是,(更多的击打),以避免破坏链接,你将需要添加一个链接作为一个类别@property和元素的的dealloc它链接到下一个元素。 而一旦我们接触到的dealloc,它只是作为好来通知元素被移除集合(这是现在正在做的)。

最后,要注意的是,在项目的测试是最小的,我可能忽略了一些东西。



Answer 5:

我刚刚创建的NSMutableDictionary和的NSMutableSet的非线程弱REF版本。 代码在这里: https://gist.github.com/4492283

对于NSMutableArray里,事情更复杂,因为它不能包含nil和一个对象可以被添加到阵列多次。 但它是实现的一个可行的。



Answer 6:

只需添加一个类别的NSMutableSet与下面的代码:

@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

同样的方法来创建NSMutableArray的和的NSMutableDictionary类别。

删除在didReceiveMemoryWarning的dealloc参考会更好。

- (void)didReceiveMemoryWarning{
    [yourWeakReferenceSet removeDeallocRef];
}

那么,你应该做的是调用addWeakReference:你的容器类。



Answer 7:

如果你是用至少的MacOS X 10.5或iOS6的,那么工作:

  • NSPointerArray weakObjectsPointerArray / pointerArrayWithWeakObjects是弱引用站在对的NSArray
  • NSHashTable hashTableWithWeakObjects / weakObjectsHashTable是弱引用站在对的NSSet
  • NSMapTable为NSDictionary的弱引用替身(可以具有弱密钥和/或弱的值)

需要注意的是集合可能不会立即注意到对象已经消失,所以计数可能仍然较高,即使关联的对象没有了键能依然存在,等等NSPointerArray有-compact方法理论上应该摆脱任何nilled指针。 NSMapTable文档注意,weakToStrong地图键将保留在表中(即使有效为零),直到将被重新调整,这意味着强大的对象指针可以保留在内存中,即使不再引用的逻辑。

编辑:我看楼主问ARC。 我认为这确实是10.8和iOS 6的容器可与ARC在使用前 - 前一个“弱”的东西是为GC,我想。 ARC是之前,不支持10.7,所以这是一个真正的问题,如果你需要支持的释放,而不是10.6,在这种情况下,你需要推出自己的(或者使用与NSPointerFunctions自定义函数,然后可以反过来用于与NSPointerArray,NSHashTable和NSMapTable)。



Answer 8:

见BMNullableArray类,这是我的BMCommons一个完整的解决这一问题框架的一部分。

这个类允许零被插入的对象,并有弱引用它所包含的对象的选项(自动nilling他们时,他们得到解除了分配)。

具有自动清除的问题(我试图执行)是你获得线程安全问题,因为它不能保证在这时间点的对象将被释放,这可能同时遍历数组以及发生。

这个类是在NSPointerArray的改进,因为它抽象为你一些较低级别的细节,并允许您使用对象而不是指针工作。 它甚至还支持NSFastEnumeration迭代与在那里零引用阵列上。



文章来源: Collections of zeroing weak references under ARC