在Swift中的同一类中实现Hashable和NSCoding

时间:2018-07-22 22:45:54

标签: swift nscoding nscoder hashable

我在尝试在同一Swift类(或结构)中采用Hashable和NSCoding协议时遇到问题。哈希性和编码/解码均独立工作。但是,一旦NSCoder恢复了对象,就失去了哈希性。

首先,我必须使它成为类而不是结构,因为显然NSCoding不适用于结构。因此,我显然需要从NSObject继承(它也恰好是可哈希的)。不幸的是,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?)

1 个答案:

答案 0 :(得分:0)

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

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

通过NSCoding解码的集合首先创建为NSSet实例,然后实例化为Swift的Set类型。 NSSet是Foundation类,因此它使用Foundation的哈希API-不幸的是,上面的TileMapCoords已被破坏,导致您注意到奇怪的行为。将hashValue替换为hash将解决此问题。