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)