为什么iOS8中的SKNode成员的[NSSet containsObject]失败? [英] Why does [NSSet containsObject] fail for SKNode members in iOS8?

查看:149
本文介绍了为什么iOS8中的SKNode成员的[NSSet containsObject]失败?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Two objects are added to an NSSet, but when I check membership, I can't find one of them.

下面的测试代码在iOS7中工作正常但在iOS8中失败。

The test code below worked fine in iOS7 but fails in 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");
}

输出:


找不到节点

could not find node

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

What happened between iOS7 and iOS8, and how can I fix it?

推荐答案

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

SKNode's implementations of isEqual and hash have changed in iOS8 to include data members of the object (and not just the memory address of the object).

收集的Apple文档警告这种情况:


如果可变对象存储在一个集合中,则哈希方法为
对象不应该依赖于可变对象
的内部状态,或者当它们在集合中时不应该修改可变对象。
例如,一个可变字典可以放在一个集合中,但你必须
在它存在时不要改变它。

If mutable objects are stored in a set, either the hash method of the objects shouldn’t depend on the internal state of the mutable objects or the mutable objects shouldn’t be modified while they’re in the set. For example, a mutable dictionary can be put in a set, but you must not change it while it is in there.

更直接地,这里


在集合对象中存储可变对象可能会导致问题。
如果
的对象包含mutate,某些集合可能会变得无效甚至损坏,因为通过变异,这些对象会影响它们放置在集合中的
的方式。

Storing mutable objects in collection objects can cause problems. Certain collections can become invalid or even corrupt if objects they contain mutate because, by mutating, these objects can affect the way they are placed in the collection.

在其他问题中描述了一般情况详细。但是,我将重复 SKNode 示例的解释,希望通过升级到iOS8来帮助那些发现此问题的人。

The general situation is described in other questions in detail. However, I'll repeat the explanation for the SKNode example, hoping it helps those who discovered this problem with the upgrade to iOS8.

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

In the example, the SKNode object changingNode is inserted into the NSSet (implemented using a hash table). The hash value of the object is computed, and it is assigned a bucket in the hash table: let's say bucket 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];

输出:


指针790756a0 hash 838599421

pointer 790756a0 hash 838599421

然后 changingNode 被修改。修改导致对象的哈希值发生更改。 (在iOS7中,更改像这样的对象 not 更改其哈希值。)

Then changingNode is modified. The modification results in a change to the object's hash value. (In iOS7, changing the object like this did not change its hash value.)

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

输出:


指针790756a0 hash 3025143289

pointer 790756a0 hash 3025143289

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

Now when containsObject is called, the computed hash value is (likely) assigned to a different bucket: say bucket 2. All objects in bucket 2 are compared to the test object using isEqual, but of course all return NO.

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

In a real-life example, the modification to changedObject probably happens elsewhere. If you try to debug at the location of the containsObject call, you might be confused to find that the collection contains an object with the exact same address and hash value as the lookup object, and yet the lookup fails.

替代实施(每个都有自己的问题)

Alternate Implementations (each with their own set of problems)


  • 仅在集合中使用不变的对象。

  • Only use unchanging objects in collections.

只有在完全控制时才将对象放入集合中,现在
和永远,他们的实现 isEqual 哈希

Only put objects in collections when you have complete control, now and forever, over their implementations of isEqual and hash.

跟踪一组(非保留)指针而不是一组对象: [NSSet setWithObject:[NSValue valueWithPointer:(void *)changingNode]]

Track a set of (non-retained) pointers rather than a set of objects: [NSSet setWithObject:[NSValue valueWithPointer:(void *)changingNode]]

使用其他集合。例如, NSArray 将受到对
isEqual 的更改的影响,但不会受到更改的影响散列。 (当然,如果
你试图保持数组的排序以便更快地查找,那么你会遇到
类似的问题。)

Use a different collection. For instance, NSArray will be affected by changes to isEqual but won't be affected by changes to hash. (Of course, if you try to keep the array sorted for quicker lookup, you'll have similar problems.)

通常这是我现实世界中最好的选择:使用 NSDictionary 其中键是 [NSValue valueWithPointer] 并且对象是保留的指针。这让我:快速查找即使对象发生变化也会有效的对象;快速删除;保留放在集合中的对象。

Often this is the best alternative for my real-world situations: Use an NSDictionary where the key is the [NSValue valueWithPointer] and the object is the retained pointer. This gives me: quick lookup of an object that will be valid even if the object changes; quick deletion; and retention of objects put in the collection.

与上一个类似,具有不同的语义和一些其他有用的选项:使用 NSMapTable ,带选项 NSMapTableObjectPointerPersonality ,以便将关键对象视为散列和相等的指针。

Similar to the last, with different semantics and some other useful options: Use an NSMapTable with option NSMapTableObjectPointerPersonality so that key objects are treated as pointers for hashing and equality.

这篇关于为什么iOS8中的SKNode成员的[NSSet containsObject]失败?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆