两个对象被添加到 NSSet ,但是当我检查成员资格时,我找不到其中一个。

下面的测试代码在 iOS7 中运行良好,但在 iOS8 中失败。

SKNode *changingNode = [SKNode node];
SKNode *unchangingNode = [SKNode node];
NSSet *nodes = [NSSet setWithObjects:unchangingNode, changingNode, nil];

changingNode.position = CGPointMake(1.0f, 1.0f);

if ([nodes containsObject:changingNode]) {
  printf("found node\n");
} else {
  printf("could not find node\n");
}

输出:



iOS7 和 iOS8 之间发生了什么,我该如何解决?

最佳答案

SKNodeisEqualhash 实现在 iOS8 中已更改,以包含对象的数据成员(而不仅仅是对象的内存地址)。

Apple documentation for collections 对这种确切情况发出警告:



而且,更直接的是 here :



in other questions in detail 描述了一般情况。但是,我会重复SKNode示例的解释,希望它可以帮助那些在升级到iOS8时发现此问题的人。

在示例中,SKNode 对象 changingNode 被插入到 NSSet 中(使用哈希表实现)。计算对象的哈希值,并在哈希表中为它分配一个桶:假设是桶 1。

SKNode *changingNode = [SKNode node];
SKNode *unchangingNode = [SKNode node];
printf("pointer %lx hash %lu\n", (uintptr_t)changingNode, (unsigned long)changingNode.hash);
NSSet *nodes = [NSSet setWithObjects:unchangingNode, changingNode, nil];

输出:



然后修改changingNode。修改会导致对象的哈希值发生更改。 (在 iOS7 中,像这样改变对象并没有改变它的哈希值。)

changingNode.position = CGPointMake(1.0f, 1.0f);
printf("pointer %lx hash %lu\n", (uintptr_t)changingNode, (unsigned long)changingNode.hash);

输出:



现在,当调用 containsObject 时,计算出的哈希值(可能)分配给不同的存储桶:比如存储桶 2。存储桶 2 中的所有对象都使用 isEqual 与测试对象进行比较,但当然都返回 NO。

在现实生活中,对 changedObject 的修改可能发生在其他地方。如果您尝试在 containsObject 调用的位置进行调试,您可能会困惑地发现该集合包含一个与查找对象具有完全相同地址和哈希值的对象,但查找失败。

替代实现 (每个都有自己的问题)
  • 只使用集合中不变的对象。
  • 仅在您完全控制时才将对象放入集合中,现在
    并且永远,在他们的 isEqualhash 实现上。
  • 跟踪一组(非保留)指针而不是一组对象:[NSSet setWithObject:[NSValue valueWithPointer:(void *)changingNode]]
  • 使用不同的集合。例如,NSArray 将受到更改的影响isEqual 但不会受到 hash 更改的影响。 (当然,如果
    您尝试对数组进行排序以加快查找速度,您将拥有
    类似的问题。)
  • 对于我的实际情况,这通常是最佳选择:使用 NSDictionary,其中键是 [NSValue valueWithPointer],对象是保留的指针。这给了我:快速查找即使对象更改也有效的对象;快速删除;并保留放入集合中的对象。
  • 与上一个类似,具有不同的语义和一些其他有用的选项:使用带有选项 NSMapTableNSMapTableObjectPointerPersonality,以便将关键对象视为散列和相等的指针。
  • 关于ios8 - 为什么 [NSSet containsObject] 在 iOS8 中对 SKNode 成员失败?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26143970/

    10-11 07:53