为什么在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