1

我想将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会将键的内存地址与字典中的现有键进行比较 - 因为尽管更改了字典的内部值(我认为),但这样的内存地址不会改变,我希望它会起作用。

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

    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"

我的问题:

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

2 回答 2

5

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

您想出的字符串技巧是一个很好的解决方法;您也可以+[NSValue valueWithNonRetainedObject:]用于此目的。由于键字典的地址在您对其进行变异时不会改变,因此您仍然可以获得相同的值以供以后查找。

于 2013-02-21T18:34:49.770 回答
2

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

在多个层面上都错了。

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

所以不行。objectForKey:相反,将消息isEqual:发送到键以决定它们是否相等。默认情况下,此方法被实施,NSObject它确实比较内存地址,但它在大多数标准集合类中被覆盖,它是明智的(for NSString, NSNumber, NSArray, NSDictionary, 等)并且它在语义上更正确,内容相关的比较.

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

于 2013-02-21T18:38:44.040 回答