2014-10-01 54 views
3

兩個對象被添加到NSSet,但是當我檢查會員資格時,我找不到其中的一個。爲什麼iOS8中的SKNode成員的[NSSet containsObject]失敗?

下面的測試代碼在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上之間發生了什麼事,我怎麼能解決這個問題?

回答

3

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]; 

輸出:

指針790756a0散列838599421

然後changingNode被修改。修改會導致對對象散列值的更改。 (在iOS7中,像這樣更改對象,而不是更改其散列值。)

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

輸出:

指針790756a0散列3025143289

現在,當containsObject被調用時,所計算的哈希值是(可能)分配給不同的桶:說桶2。桶2中的所有對象都與使用isEqual的測試對象進行比較,但當然全部返回NO。

在一個現實生活中的例子中,對changedObject的修改可能發生在其他地方。如果嘗試在調用containsObject的位置進行調試,可能會發現該集合包含的對象與查找對象的地址和哈希值完全相同,但查找失敗。

可選的實施方式(每個都有自己的一套問題)

  • 只有在集合使用不變的對象。

  • 只有在完全控制時纔將對象置於集合中,現在將永久性地將對象置於集合中,其實現方式爲isEqualhash

  • 跟蹤一組(非保留)的指針,而不是一組對象:[NSSet setWithObject:[NSValue valueWithPointer:(void *)changingNode]]

  • 使用一個不同的集合。例如,NSArray將受到對 isEqual的更改的影響,但不會受hash更改的影響。 (當然,如果你 儘量保持整理更快查找數組,你必須 類似的問題。)

  • 通常這是我的真實世界的情況下,最好的選擇:使用NSDictionary其中的關鍵是[NSValue valueWithPointer],對象是保留的指針。這給了我:快速查找即使對象更改也會有效的對象;快速刪除;並保留收藏中的物品。

  • 與上一個類似,具有不同的語義和一些其他有用的選項:使用NSMapTable和選項NSMapTableObjectPointerPersonality,以便將關鍵對象視爲散列和相等的指針。