我想将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"
。
我的问题:
答案 0 :(得分:5)
字典在设置时会获取其密钥的副本(并且该副本是不可变的)。然后当您改变用于键的对象时,它和副本不再相等:NSDictionary
在检查键时使用hash
和isEqual:
。 (集合不会在这些方法中使用对象的地址来确定它们是否相等,即使它们确实相同,副本和原始地址也有不同的地址。)因此,您无法检索该值。如果你将字典重新改变为它,那么检索就会成功。
你提出的字符串技巧是一个很好的解决方法;您也可以使用+[NSValue valueWithNonRetainedObject:]
来实现此目的。由于当您只是 mutate 时,密钥字典的地址不会改变,因此您仍然可以获得相同的值以便在之后查找。
答案 1 :(得分:2)
我假设objectForKey会将密钥的内存地址与字典中的现有密钥进行比较
多个级别错误。
如果通过内存地址比较密钥,则没有可靠的方法来获取密钥的对象。如果您使用常量字符串@"Foo"
作为键,并且在运行时,您传递了另一个NSString
实例,内容为Foo
,但它是一个不同的对象(因此它的内存地址是完全不同)?
所以没有。 objectForKey:
所做的是将isEqual:
消息发送给密钥以确定它们是否相等。默认情况下,此方法在NSObject
中被强制执行,它确实比较了内存地址,但它在大多数标准集合类中被覆盖(对于NSString
,NSNumber
,{{ 1}},NSArray
等)并且它在语义上做了更正确的,与内容相关的比较。
此外,当设置了密钥对象对时,NSDictionary
的密钥被复制。这意味着如果使用直接指针比较,根本无法获取任何对象,,因为副本是一个不同的对象,另一个实际的对象实例,指针指向在其他地方(显然)。 (旁注:对于像NSDicionary
这样的一些不可变的Cocoa类来说,这并不完全正确,其中NSString
基本上是使用copy
实现的,但你应该得到这个概念。)