为什么在iOS8中[NSSet containsObject]对SKNode成员失败?

为什么在iOS8中[NSSet containsObject]对SKNode成员失败?,ios8,sprite-kit,nsset,sknode,Ios8,Sprite Kit,Nsset,Sknode,两个对象被添加到NSSet,但当我检查成员资格时,找不到其中一个 下面的测试代码在iOS7中运行良好,但在iOS8中失败 SKNode*changingNode=[SKNode]; SKNode*不变节点=[SKNode]; NSSet*节点=[NSSet setWithObjects:unchangingNode,changingNode,nil]; changingNode.position=CGPointMake(1.0f,1.0f); if([节点包含对象:changingNode]){

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

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

SKNode*changingNode=[SKNode];
SKNode*不变节点=[SKNode];
NSSet*节点=[NSSet setWithObjects:unchangingNode,changingNode,nil];
changingNode.position=CGPointMake(1.0f,1.0f);
if([节点包含对象:changingNode]){
printf(“找到的节点\n”);
}否则{
printf(“找不到节点\n”);
}
输出:

找不到节点


iOS7和iOS8之间发生了什么,我如何修复它?

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

政府警告人们要注意这种情况:

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

更直接的是:

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

描述了总体情况。但是,我将重复对
SKNode
示例的解释,希望它能帮助那些在升级到iOS8时发现这个问题的人

在该示例中,将
SKNode
对象
changingNode
插入
NSSet
(使用哈希表实现)。计算对象的哈希值,并在哈希表中为其分配一个bucket:比如bucket 1

SKNode*changingNode=[SKNode];
SKNode*不变节点=[SKNode];
printf(“指针%lx散列%lu\n”,(uintpttr\t)changingNode,(unsigned long)changingNode.hash);
NSSet*节点=[NSSet setWithObjects:unchangingNode,changingNode,nil];
输出:

指针790756a0散列838599421

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

changingNode.position=CGPointMake(1.0f,1.0f);
printf(“指针%lx散列%lu\n”,(uintpttr\t)changingNode,(unsigned long)changingNode.hash);
输出:

指针790756a0散列3025143289

现在,当调用
containsObject
时,计算出的哈希值(很可能)被分配到另一个bucket:比如bucket 2。使用
isEqual
,将bucket 2中的所有对象与测试对象进行比较,当然,所有对象都返回NO

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

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

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

  • 现在,只有在拥有完全控制权时才将对象放入集合中 而且永远,在他们的
    isEqual
    hash
    实现之上

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

  • 使用不同的集合。例如,
    NSArray
    将受到
    isEqual
    ,但不会受到对
    哈希的更改的影响。(当然,如果
    如果您尝试对数组进行排序,以便更快地查找,则
    类似的问题。)

  • 对于我的现实情况,这通常是最好的选择:使用
    NSDictionary
    ,其中键是
    [NSValue valueWithPointer]
    ,对象是保留的指针。这给了我:快速查找一个即使对象发生变化也有效的对象;快速删除;以及保留放置在集合中的对象

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