【问题标题】:Why don't NSSet/NSMutableSet/NSCountedSet force immutable objects as entries?为什么 NSSet/NSMutableSet/NSCountedSet 不强制不可变对象作为条目?
【发布时间】:2015-02-11 18:27:01
【问题描述】:

NSDictionary 键是 id 但集合的值只是 id,并且文档表明它们的值被保留。根据Set Fundamentals of the Collection Programming Topics 文档:

但是,您可以自己修改单个对象(如果它们支持修改)。

如果您修改一个对象,这可能会影响该对象的哈希值,从而影响查找。我假设 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
)}

我的看法:

集合可能基于哈希值的桶,当哈希在集合后面被改变时,它不知道该怎么做并且查找被破坏。这方面缺乏文档。

我的问题重申: 文档说你可以改变对象,这并不直观。 变异对象会破坏集合。 什么鬼?

【问题讨论】:

  • 只有当你改变参与等于和散列确定的值时,它才会破坏集合。而禁止集合元素的变异会严重限制函数的实用性。

标签: objective-c cocoa


【解决方案1】:

文档中的那一行令人困惑。但是,请注意下面的三段继续说:

如果可变对象存储在集合中,则可以使用 hash 方法 对象不应该依赖于可变对象的内部状态 或者可变对象在集合中时不应该被修改。 例如,一个可变字典可以放在一个集合中,但你必须 当它在那里时不要改变它。 (请注意,可能很难 知道给定对象是否在集合中)。

您的代码展示的是基于散列的集合类的已知属性。它也会影响字典,如果一个键对象被实现为复制返回原来的对象,它本质上是可变的。

没有真正的方法可以测试对象是否可变。所以,它不能强制不变。

此外,正如上面引用中提到的,可以创建一个可变类,其hash 和相等性不受突变影响。

最后,如果这些集合类只能与可复制类一起使用并复制元素(就像字典复制它们的键一样),那将严重限制这些集合类的实用性。集合用于表示关系等,如果您尝试在对象之间建立关系,而是与单独的副本建立关系,则它不会这样做。

【讨论】:

  • 那段总是让我很困惑;根据内容/状态,您如何在没有它的情况下对某些内容进行哈希处理?
  • 哈希应该反映导致平等的相同因素。假设您的对象中有一些元数据类型字段、缓存、派生值等。您可以更改这些,从技术上讲,这会更改状态,但不会影响对象中对相等性重要的部分。
【解决方案2】:

由于在 Objective-C 中确保对象不变性的唯一可靠方法是制作副本,因此 Cocoa 设计人员有两种选择:

  • NSSet 复制对象 - 这很安全,但由于内存使用量增加,会严重限制NSSet 的使用。
  • 使用保留对象 - 这样可以将内存使用量保持在最低限度,但它会为用户提供一种方法,通过在 NSSet 中改变一个对象来自爆。

设计师选择了第二种方法而不是第一种方法,因为它解决了一个可以通过适当的编码技术避免的危险。相比之下,选择第一种方法对每个人都是“绑定”的,因为插入一个新对象总是会产生一个副本。

目前,用户可以选择插入他们手动创建的对象的副本,从而模拟第一种方法。但是,强制复制的实现无法模拟保留对象的实现,这使其成为不太灵活的选择。

【讨论】:

  • 为什么文档不概述使用保留对象的缺点?文档说你可以做到,这对新开发者真的没有帮助。
  • @KirkSpaziani - 假设使用 Objective-C 的开发人员能够理解这些含义,而无需列举它们。
  • @KirkSpaziani 文档确实概述了它,但在不同的地方。他们没有在所有集合的文档中提及它,而是说一次in the docs of NSObject's hash method
  • 最后一段似乎是最清晰的解释:通过常规 NS[Mutable]Set 的组合来制作 SetWhatCopiesItsMembers 非常简单;编写一个全新的仅保留集类并没有那么有趣。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多