1

我试图弄清楚这段代码引用:Cocoa: Dictionary with enum keys?

+ (NSValue*)valueWithReference:(id)target
{
    return [NSValue valueWithBytes:&target objCType:@encode(id*)];
}

和,

[table setObject:anObject forKey:[NSValue valueWithReference:keyObject]];

但是感觉有些不太好。有什么建议吗?

4

3 回答 3

9

你说得对,这不好。

一方面,您编码的类型错误(应该是@encode(id),而不是@encode(id*)),但在大多数情况下,这不会造成大问题。

更大的问题是这完全忽略了内存管理。该对象不会被保留或复制。如果其他一些代码释放它,它可能会消失,然后你的字典键将是一个指向垃圾的盒装指针,甚至是一个完全不同的对象。这基本上是世界上最先进的悬空指针

你有两个不错的选择:

  1. 您可以将 NSCopying 添加到该类或创建一个可复制的子类。

    • 此选项仅适用于可以有意义地复制的对象。这是大多数类,但不一定是所有类(例如,让多个对象表示相同的输入流可能很糟糕)
    • 即使对于有意义的类来说,实现复制也可能是一种痛苦——本身并不困难,但有点烦人
  2. 您可以改为使用CFDictionary API创建字典。由于 Core Foundation 类型没有通用的复制功能,CFDictionary 默认只保留它的键(尽管你可以随意定制它的行为)。但是 CFDictionary 也是与 NSDictionary 的免费桥接,这意味着您可以将 a 强制CFDictionaryRef转换为NSDictionary*(or NSMutableDictionary*),然后像对待任何其他 NSDictionary 一样对待它。

    • 这意味着您用作键的对象在字典中时不得更改(至少不会以影响其hash值的方式) - 确保不会发生这种情况是 NSDictionary 通常想要复制其键的原因
于 2010-08-18T06:06:32.613 回答
1

供以后参考。

现在我知道还有更多选择。

  1. 覆盖NSCopying协议中的方法,并返回self而不是复制自身。(如果您不使用 ARC,则应保留它)此外,您还要确保对象始终为-hash方法返回相同的值。

  2. 使可复制的简单容器类持有对原始键对象的强引用。容器是可复制的,但它只是在复制时传递原始密钥。覆盖相等/散列方法也匹配语义。即使只是一个NSArray只包含关键对象的实例也能很好地工作。

方法 #1 看起来很安全,但实际上我不确定这是否安全。因为我不知道NSDictionary. 所以我通常使用 #2 方式,这在 Cocoa 约定中是完全安全的。

更新

现在我们已经有了NSHashTable,并且NSMapTable自 6.0 版以来也在 iOS 中。

于 2012-07-21T16:42:56.083 回答
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)。

这似乎工作正常,但可能会有一些我没有想到的不需要的副作用。如果有人发现此解决方案有任何其他问题或警告,请发表评论。

于 2012-02-23T20:42:06.723 回答