我怎样才能获得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