我试图弄清楚这段代码的引用:Cocoa: Dictionary with enum keys?
+ (NSValue*)valueWithReference:(id)target
{
return [NSValue valueWithBytes:&target objCType:@encode(id*)];
}
和
[table setObject:anObject forKey:[NSValue valueWithReference:keyObject]];
但感觉不好。有什么建议吗?
答案 0 :(得分:9)
你说得对,这不好。
首先,您编码的是错误的类型(它应该是@encode(id)
,而不是@encode(id*)
),但在大多数情况下,这不会导致大问题。
更大的问题是,这完全忽略了内存管理。该对象不会被保留或复制。如果其他一些代码释放它,它可能会消失,然后你的字典键将是一个盒装指针,指向垃圾甚至完全不同的对象。这基本上是世界上最先进的dangling pointer。
您有两个不错的选择:
您可以将NSCopying添加到类中,也可以创建可复制的子类。
您可以使用CFDictionary API创建字典。由于Core Foundation类型没有通用的复制功能,因此CFDictionary默认只保留其键(尽管您可以根据需要自定义其行为)。但CFDictionary也与NSDictionary免费联系,这意味着您可以将CFDictionaryRef
转换为NSDictionary*
(或NSMutableDictionary*
),然后将其视为任何其他NSDictionary。
hash
值),而它在字典中 - 确保不会发生这种情况是为什么NSDictionary通常想要复制其密钥答案 1 :(得分:1)
供以后参考。
现在我知道还有更多选择。
覆盖NSCopying
协议中的方法,并返回self
而不是复制自身。 (如果您不使用ARC,则应保留它)同时确保对象始终为-hash
方法返回相同的值。
使可复制的简单容器类保持对原始密钥对象的强引用。容器是可复制的,但它只是在复制时传递原始密钥。覆盖等式/哈希方法也匹配语义。即使只是NSArray
的实例,也只包含关键对象。
方法#1看起来很安全,但实际上我不确定这是安全的。因为我不知道NSDictionary
的内部行为。所以我通常使用#2方式,这在Cocoa约定中是完全安全的。
<强>更新强>
现在,自版本6.0起,我们在iOS中也有NSHashTable
和NSMapTable
。
答案 2 :(得分:0)
我不是100%肯定这个解决方案的正确性,但我发布它是为了以防万一。
如果你不想使用CFDictionary,也许你可以使用这个简单的类别:
@implementation NSMutableDictionary(NonCopyableKeys)
- (void)setObject:(id)anObject forNonCopyableKey:(id)aKey {
[self setObject:anObject forKey:[NSValue valueWithPointer:aKey]];
}
- (id)objectForNonCopyableKey:(id)aKey {
return [self objectForKey:[NSValue valueWithPointer:aKey]];
}
- (void)removeObjectForNonCopyableKey:(id)aKey {
[self removeObjectForKey:[NSValue valueWithPointer:aKey]];
}
@end
这是我在网上看到的类似方法(无法找到原始资源)的概括,用于使用可以使用UITouch密钥存储对象的NSMutableDictionary。
与Chuck的答案相同的限制适用:您用作键的对象不得以影响其哈希值的方式更改,并且在字典中不得释放。
另外请确保不要混用-(void)setObject:(id)anObject forNonCopyableKey:(id)aKey
和- (id)objectForKey:(id)aKey
方法,因为它不起作用(后者将返回nil
)。
这似乎工作正常,但可能会有一些我不想的副作用。如果有人发现此解决方案有任何其他问题或警告,请发表评论。