【问题标题】:Implementing Hashable and NSCoding in the same class in Swift在 Swift 的同一个类中实现 Hashable 和 NSCoding
【发布时间】:2018-07-22 22:45:54
【问题描述】:

我在尝试在同一个 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 集中的对象相同,'contains' 方法会告诉我

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

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

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

【问题讨论】:

  • 你能解释一下为什么你需要实现 NSCoding 吗? Codable 的全部意义在于它可以轻松地对结构进行编码,并且在任何情况下都不会使用 NSCoding 而不能使用 Codable。那么为什么不直接使用 Codable 呢?
  • 您的代码对我来说工作得很好,无需在解码后构建第二组。请使用完全可重现的示例来说明您的问题,更新您的问题。
  • 代码工作正常,直到我对集合进行编码然后对其进行解码。然后“包含”方法似乎不再在集合中找到(相同的)对象。
  • 好吧,老实说我不知道​​ Codable。如果我可以将它与结构一起使用,那将是完美的。我会努力实现的。
  • 正如我所说,您的代码对我有用。即使在编码和解码之后。这就是为什么您需要发布一个完整的工作示例来演示该问题。将可以按原样复制和粘贴的内容发布到 Playground 中。

标签: swift nscoding nscoder hashable


【解决方案1】:

要在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 将解决此问题。

【讨论】:

    猜你喜欢
    • 2014-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多