Swift哈希(到:)可哈希协议要求的缓存结果

Swift哈希(到:)可哈希协议要求的缓存结果,swift,hash,hashable,Swift,Hash,Hashable,我有一门课被大量地用在集合和字典中。 出于性能原因,此类以旧的方式实现Hashable并缓存计算的哈希: let hashValue: Int init(...) { self.hashValue = ... } 在Xcode 10.2中,我看到一个警告,hashValue已被弃用,不久将不再是协议要求 困扰我的是,无论如何都无法缓存计算出的哈希,因为hashinto:不返回任何内容 func hash(into hasher: inout Hasher) { hasher.

我有一门课被大量地用在集合和字典中。 出于性能原因,此类以旧的方式实现Hashable并缓存计算的哈希:

let hashValue: Int

init(...) {
    self.hashValue = ...
}
在Xcode 10.2中,我看到一个警告,hashValue已被弃用,不久将不再是协议要求

困扰我的是,无论如何都无法缓存计算出的哈希,因为hashinto:不返回任何内容

func hash(into hasher: inout Hasher) {
    hasher.combine(...)
}
考虑以下在操场上的例子

class Class: Hashable {
    let param: Int

    init(param: Int) {
        self.param = param
    }

    static func ==(lhs: Class, rhs: Class) -> Bool {
        return lhs.param == rhs.param
    }

    public func hash(into hasher: inout Hasher) {
        print("in hash")
        hasher.combine(param)
    }
}

var dict = [Class: Int]()
let instance = Class(param: 1)
dict[instance] = 1
dict[instance] = 2
您将看到以下日志

in hash
in hash
in hash
我不知道,为什么我们看到的是3个呼叫而不是2个,但我们看到的是=

因此,每次使用同一实例作为字典键或将该实例添加到集合中时,都会得到一个新的hashinto:call


在我的代码中,这样的开销是非常昂贵的。有人知道一种解决方法吗?

一种方法是创建自己的哈希程序,将实例的基本组件提供给它,然后调用它以获取可以缓存的Int哈希值

例如:

class C : Hashable {
  let param: Int

  private lazy var cachedHashValue: Int = {
    var hasher = Hasher()
    hasher.combine(param)
    // ... repeat for other "essential components"
    return hasher.finalize()
  }()

  init(param: Int) {
    self.param = param
  }

  static func ==(lhs: C, rhs: C) -> Bool {
    return lhs.param == rhs.param
  }

  public func hash(into hasher: inout Hasher) {
    hasher.combine(cachedHashValue)
  }
}
关于这一点,需要注意以下几点:

它依赖于您的基本组件是不可变的,否则一个新的哈希值将需要在变异时计算。 哈希值不能保证在程序执行期间保持稳定,所以不要序列化cachedHashValue。
显然,在存储单个Int的情况下,这并不是很有效,但对于更昂贵的实例,这很可能有助于提高性能。

看起来像一个黑客=我会测量它并稍后共享一个报告=鉴于Hasher应该如何使用,我不会真的称它为黑客。我承认这比使用hashValue要复杂一些,但是,正如Martin所说,新的API允许更健壮的hash值。