-1

我在尝试在同一个 Swift 类(或结构)中采用 Hashable 和 NSCoding 协议时遇到问题。哈希性和编码/解码都独立工作。然而,一旦一个对象被 NSCoder 恢复,散列性就会丢失。

首先,我必须将其设为类而不是结构,因为显然 NSCoding 不适用于结构。因此,我显然需要从 NSObject 继承(它也恰好是 Hashable)。不幸的是,NSObject 的这种内置散列性似乎阻止了我以我想要的方式使我自己的类可散列(我认为。)

这是我的课:

class TileMapCoords : NSObject, NSCoding {

var row: Int
var column: Int

init(row: Int, column: Int) {
    self.row = row
    self.column = column
}

// Hashable
override var hashValue: Int {
    return column*numTMRows + row
}

static func == (lhs: TileMapCoords, rhs: TileMapCoords) -> Bool {
    return
        lhs.row == rhs.row &&
            lhs.column == rhs.column
}

// do I even need this?
override func isEqual(_ object: Any?) -> Bool {

    if let otherCoords = object as? TileMapCoords {
        return self == otherCoords
    } else  {
        return false
    }
}

// NSCoding
required init?(coder aDecoder: NSCoder) {
    row = aDecoder.decodeInteger(forKey: "TileMapCoords.row")
    column = aDecoder.decodeInteger(forKey: "TileMapCoords.column")
}

func encode(with aCoder: NSCoder) {
    aCoder.encode(row, forKey: "TileMapCoords.row")
    aCoder.encode(column, forKey: "TileMapCoords.column")
}

}

我正在创建一组这些对象并对它们进行编码/解码,如下所示:

编码:

aCoder.encode(itemsCollected, forKey: "GameData.itemsCollected")

解码:

var itemsCollected = Set<TileMapCoords>()
itemsCollected = aDecoder.decodeObject(forKey: "GameData.itemsCollected") as! Set<TileMapCoords>

但后来我发现集合中的对象在解码时的哈希值与编码时的哈希值不同。所以我做了一个我认为有效的解决方法:

let itemsCollectedCopy = aDecoder.decodeObject(forKey: "GameData.itemsCollected") as! Set<TileMapCoords>

var itemsCollected = Set<TileMapCoords>()
for coords in itemsCollectedCopy {
        itemsCollected.insert(coords) }

当我将它们打印出来时,这个“技巧”似乎纠正了散列值,但是对象仍然没有被正确散列(原因未知)。即当我这样做时:

if curGameData!.itemsCollected.contains(location) {
     // do something      
 }

如果我创建一个 TileMapCoords 对象,该对象与 itemsCollected 集中的对象具有相同的行和列,则“包含”方法会告诉我

1) 最初在集合中 2) 一旦集合已被 NSCoder 恢复/解码,则不在集合中

奇怪的是对象在集合中,我已经验证我的对象和集合中的对象都具有相同的哈希值,并且根据 == 运算符和 isEqual 方法相等。

我需要帮助解决这个问题或考虑另一种方法在同一个类中同时具有哈希性和 NSCodability(也许不覆盖 NSObject?)

4

1 回答 1

0

要在子类中自定义散列和相等性NSObject,您需要覆盖hash属性和isEqual(_:)方法。

虽然不幸的是 Swift-onlyNSObject.hashValue属性是可覆盖的(从 Swift 4.1 开始),但您绝不能覆盖它。而是重写hash,以便 Foundation 选择正确的散列行为——否则 Foundation 将使用继承自 的实现NSObject,它基于对象身份。这不适用于TileMapCoords具有自定义isEqual(_:)实现的 。

通过解码的集合NSCoding首先创建为NSSet实例,然后桥接到 Swift 的Set类型。NSSet是一个 Foundation 类,因此它使用 Foundation 的哈希 API——不幸的是,它在TileMapCoords上面被破坏了,导致你注意到的奇怪行为。替换hashValuehash将解决问题。

于 2018-07-31T17:41:57.137 回答