如何有效地使用NSMutableDictionaries作为字典的键?

时间:2013-02-21 18:32:01

标签: ios objective-c nsdictionary nsmutabledictionary

我想将NSMutableDictionary个实例用作另一个NSMutableDictionary的键。

    NSMutableDictionary *stuff = [NSMutableDictionary dictionary];
    NSMutableDictionary *first = [NSMutableDictionary dictionary];
    [stuff setObject:@"A" forKey:first];
    NSLog(@"I GOT %@",[stuff objectForKey:first]);

按预期输出@"A",这很好。

但是,如果之后我修改了first对象中的值,如下所示:

[first setObject:@"C" forKey:@"Something"];

我现在无法获得@"A"

NSLog(@"I GOT %@",[stuff objectForKey:first]);

哪个输出nil

我假设objectForKey会将密钥的内存地址与字典中的现有密钥进行比较 - 因为尽管更改了字典的内部值(我认为),但这样的内存地址不会改变,我预计它会起作用。

我设法让它使用不同的方法 - 而不是使用NSString s作为键 - 字符串的值将是内存地址本身,如下所示:

    NSMutableDictionary *stuff = [NSMutableDictionary dictionary];
    NSMutableDictionary *first = [NSMutableDictionary dictionary];
    [stuff setObject:@"A" forKey:[NSString stringWithFormat:@"%p",first]];
    [first setObject:@"C" forKey:@"Something"];
    NSLog(@"I GOT %@",[NSString stringWithFormat:@"%p",first]);

哪种方法有效,打印@"A"

我的问题:

  • 为什么我原来的方法不起作用?

2 个答案:

答案 0 :(得分:5)

字典在设置时会获取其密钥的副本(并且该副本是不可变的)。然后当您改变用于键的对象时,它和副本不再相等:NSDictionary在检查键时使用hashisEqual:。 (集合不会在这些方法中使用对象的地址来确定它们是否相等,即使它们确实相同,副本和原始地址也有不同的地址。)因此,您无法检索该值。如果你将字典重新改变为它,那么检索就会成功。

你提出的字符串技巧是一个很好的解决方法;您也可以使用+[NSValue valueWithNonRetainedObject:]来实现此目的。由于当您只是 mutate 时,密钥字典的地址不会改变,因此您仍然可以获得相同的值以便在之后查找。

答案 1 :(得分:2)

  

我假设objectForKey会将密钥的内存地址与字典中的现有密钥进行比较

多个级别错误。

如果通过内存地址比较密钥,则没有可靠的方法来获取密钥的对象。如果您使用常量字符串@"Foo"作为键,并且在运行时,您传递了另一个NSString实例,内容为Foo,但它是一个不同的对象(因此它的内存地址是完全不同)?

所以没有。 objectForKey:所做的是将isEqual:消息发送给密钥以确定它们是否相等。默认情况下,此方法在NSObject中被强制执行,它确实比较了内存​​地址,但它在大多数标准集合类中被覆盖(对于NSStringNSNumber,{{ 1}},NSArray等)并且它在语义上做了更正确的,与内容相关的比较。

此外,当设置了密钥对象对时,NSDictionary的密钥被复制。这意味着如果使用直接指针比较,根本无法获取任何对象,,因为副本是一个不同的对象,另一个实际的对象实例,指针指向在其他地方(显然)。 (旁注:对于像NSDicionary这样的一些不可变的Cocoa类来说,这并不完全正确,其中NSString基本上是使用copy实现的,但你应该得到这个概念。)