Cocoa的NSDictionary:为什么要复制密钥?

时间:2010-03-06 21:00:50

标签: cocoa data-structures nsdictionary nsmutabledictionary

在NS(Mutable)词典中用作键的所有对象必须支持NSCopying协议,并且这些对象在字典中使用时会被复制。

我经常想要使用较重的物体作为键,只需将一个物体映射到另一个物体。当我这样做时,我真正的意思是:

[dictionary setObject:someObject forKey:[NSValue valueWithPointer:keyObject]];

(“当我回来再把你这个相同的关键对象实例交给你时,给我相同的价值。”)

...这正是我最终做的有时候绕过这个设计。 (是的,我知道桌面Cocoa中的NSMapTable;但是,例如iPhone不支持此功能。)

但是我真正得到的是为什么首先复制密钥是必要的或可取的。它是如何购买实现或调用者的?

3 个答案:

答案 0 :(得分:26)

该副本确保用作键的值在用作键时不会“改变”。考虑一个可变字符串的例子:

NSMutableString* key = ... 
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];

[dict setObject: ... forKey: key];

我们假设字典没有复制密钥,而只是retain编辑它。如果现在,在稍后的某个时刻,原始字符串被修改,那么即使您使用相同的密钥对象(即{{1}),您很可能不会再次在字典中找到存储的值。在上面的例子中指出。)

为了保护自己免受这样的错误,字典会复制所有密钥。

顺便说一句,请注意,将key定义为-copyWithZone:就足够了。如果您的对象是不可变的,那么这是允许的和良好的代码,并且return [self retain]契约是专门设计的,使得返回的对象必须是(sorta,kinda)不可变的:

  

通过保留原始内容而不是在类及其内容不可变时创建新副本来实现NSCopying。

(来自NSCopying Reference

  

如果考虑“immutable vs. mutable”适用于接收对象,则返回的副本是不可变的;否则副本的确切性质由班级决定。

(来自-copyWithZone: Reference

即使你的对象不是不可变的,如果你只使用基于身份的相等/散列实现,即对象的内部状态不受任何影响的实现,你可能会逃避该实现。

答案 1 :(得分:7)

如果要将指针存储为键,则需要将它们包含在NSValue的{​​{1}}对象中。

答案 2 :(得分:4)

从iOS 6开始,如果你想使用指针作为键,你可以使用NSMapTable对象,参见http://nshipster.com/nshashtable-and-nsmaptable/

您可以指定键和/或值是强制还是弱持有:

NSMapTable *mapTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory
                                         valueOptions:NSMapTableWeakMemory];

有时候另一个适当的选择是使用NSCache,它强有力地保存密钥并且实际上是线程安全的。