Cocoa 可可';NSS字典:为什么要复制密钥?

Cocoa 可可';NSS字典:为什么要复制密钥?,cocoa,data-structures,nsdictionary,nsmutabledictionary,Cocoa,Data Structures,Nsdictionary,Nsmutabledictionary,在NS(可变)字典中用作键的所有对象都必须支持NSCopying协议,并且这些对象在字典中使用时会被复制 我经常想使用较重的权重对象作为关键点,简单地将一个对象映射到另一个对象。我这样做的真正意思是: [dictionary setObject:someObject forKey:[NSValue valueWithPointer:keyObject]]; (“当我再次返回并将相同的键对象实例交给您时,请将相同的值输出给我。”) …这正是我有时为了绕过这个设计而做的。(是的,我知道deskto

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

我经常想使用较重的权重对象作为关键点,简单地将一个对象映射到另一个对象。我这样做的真正意思是:

[dictionary setObject:someObject forKey:[NSValue valueWithPointer:keyObject]];
(“当我再次返回并将相同的键对象实例交给您时,请将相同的值输出给我。”)

…这正是我有时为了绕过这个设计而做的。(是的,我知道desktop Cocoa中的NSMapTable;但例如iPhone不支持此功能。)


但我真正不明白的是,为什么复制密钥首先是必要的或可取的。它为实现或调用方购买了什么

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

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

[dict setObject: ... forKey: key];
让我们假设字典没有复制密钥,而是
保留它。如果现在,在以后的某个时候,原始字符串被修改,那么很可能您不会在字典中再次找到存储的值,即使您使用的是非常相同的键对象(即,上面示例中的
key
指向)

为了防止自己犯这样的错误,字典会复制所有的钥匙

请注意,顺便说一下,将
-copyWithZone:
定义为只执行
返回[self-retain]
非常简单。如果您的对象是不可变的,那么这是允许的,也是好代码,并且
NSCopying
契约是专门设计的,因此返回的对象必须(排序,有点)不可变:

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

(来自)

如果考虑“不可变与可变”适用于接收对象,则返回的副本是不可变的;否则,副本的确切性质由类决定

(来自)


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

如果要将指针存储为键,则需要将它们封装在
NSValue
对象中,并使用
+valueWithPointer:
自iOS 6以来,如果要将指针用作键,可以使用对象,请参阅

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

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

另一个有时可能合适的选择是使用,它可以很好地保存密钥,并且实际上是线程安全的

德克:谢谢你。我认为我误解的关键事实(你的回答让我误解)是键的语义与键对象ref无关;它们是关于键的内容(因此键是字符串“foo”,不管它是从文本还是从chars构造的——这意味着语义比较,而不是指针比较。但是密钥必须被复制,因为可变密钥可能会改变。这给我留下的印象是,当我真的想用一个对象作为一个键,并且我真的在追求引用相等时,我的方法不是一个黑客;完全正确。本:不完全正确。如果希望保留
keyObject
,则该代码不会这样做,因为它将
keyObject
视为指向任何对象的指针,而不一定是对象,因此无法知道它可以保留它。我不确定
value:withObjCType:
是否会保留,即使传递
@encode(id)
。为了绝对确保它保留了您的密钥,您需要使用NSMapTable(您意味着您不能这样做,因为您是为iPhone开发的)、使用CFDictionary而不是NSDictionary(无论您在哪里使用dict,而不仅仅是在创建dict时),或者创建自己的NSValue子类。这一点很好。谢谢你提供的额外信息。正如我所想,当我使用这个模式时,我从不需要保留键。通常,其他一些集合实际上“拥有”了我用作键的对象;我只是用字典把它们有效地映射到其他相关的对象。最坏的情况是键对象将被释放,在字典中留下一个turd条目。我唯一需要注意的是枚举实际的键并将指针返回到
id
s。但那段代码闻起来很难闻,足以让人知道你在做一些危险的事情……如果我错了,请纠正我,但当我尝试实现它时,它没有起作用,因为我的指针引用的对象在我想要访问它时被释放了。我使用了
+valueWithNonretainedObject: