NSDictionary键是id< NSCopying>但是set的值只是id,而docs表明它们的值是保留的。根据{{3}}文档:
但是,您可以自行修改单个对象(如果它们支持修改)。
如果修改对象,则可能会影响对象的哈希值,这会影响查找。我假设NSSet是一个快速查找?
这是一个示例,展示了如果您改变对象时事情如何破坏:
NSMutableString *str = [NSMutableString stringWithString: @"AWESOME"];
NSCountedSet *countedSet = [[NSCountedSet alloc] init];
[countedSet addObject: str];
[countedSet addObject: str];
NSLog(@"%@", @([countedSet countForObject: @"AWESOME"]));
[str appendString: @" NOT AWESOME"];
NSLog(@"%@", @([countedSet countForObject: @"AWESOME NOT AWESOME"]));
NSLog(@"%@", @([countedSet countForObject: @"AWESOME"]));
NSLog(@"%@", @([countedSet countForObject: str]));
for(NSString *s in countedSet) {
NSLog(@"%@ - %@", str, @([countedSet countForObject: s]));
}
NSSet *set = [NSSet setWithArray: @[ str ]];
NSLog(@"Set Contains string, %@", @([set containsObject: str]));
[str appendString: @"asdf"];
NSLog(@"Set Contains string, %@", @([set containsObject: str]));
NSLog(@"%@", set);
输出我的解释:
[64844:303] 2 // Count is 2
[64844:303] 0 // Count should be 2 - if it looks for the literal string
[64844:303] 0 // Count should be 0, but can't find original object either
[64844:303] 0 // Count should be 2 - asking for actual object that's in there
[64844:303] AWESOME NOT AWESOME - 0 // Should be 2 - asking for actual object that it just retrieved
[64844:303] Set Contains string, 1 // Correct, pre-mutation
[64844:303] Set Contains string, 0 // Should be true, object is in there
[65070:303] {(
"AWESOME NOT AWESOMEasdf" // see? It's in there
)}
我的看法:
该集可能基于散列值的桶,当散列在集合后面更改时,它不知道该做什么以及查找被破坏。该领域缺乏文档。
我的问题重申: 文档说你可以改变对象,这是不直观的。 变异对象会破坏集合。 WTF?
答案 0 :(得分:3)
来自文档的那一行令人困惑。然而,请注意它下面的三个段落继续说:
如果可变对象存储在集合中,则可以使用
hash
方法 对象不应该依赖于可变对象的内部状态 或者可变对象在它们处于集合中时不应被修改。 例如,可变字典可以放在一个集合中,但您必须 当它在那里时不要改变它。 (注意,它可能很难 知道给定对象是否在集合中。)
您的代码演示的内容是基于散列的集合类的已知属性。如果实现了一个关键对象,复制会返回原始内容,这本身就是可变的,也会影响字典。
没有真正的方法来测试对象是否可变。所以,它不能强迫不变性。
另外,正如上面引用中所提到的,可以创建一个hash
和等式不受突变影响的可变类。
最后,如果它们只能与可复制类一起使用并制作元素的副本(如字典复制其键),则会严重限制这些集合类的实用程序。这些集合用于表示关系等,如果您尝试在对象之间建立关系,而不是建立与单独副本的关系,则不会这样做。
答案 1 :(得分:2)
由于在Objective-C中确保对象不变性的唯一可靠方法是制作副本,Cocoa设计师有两种选择:
NSSet
复制对象 - 这样会安全,因为内存使用量增加会严重限制NSSet
的使用。NSSet
内的对象来自己射击。 / LI>
设计师在第一种方法中选择了第二种方法,因为它解决了通过适当的编码技术可以避免的危险。相比之下,选择第一种方法将是“绑定”每个人,因为插入新对象总是会复制。
目前,用户可以选择插入他们手动创建的对象副本,从而模拟第一种方法。但是,强制复制的实现无法模拟保留对象的实现,从而使其成为不太灵活的选择。