首页 > 解决方案 > 如何在 Xcode 9.4 中使 Optionals 可散列

问题描述

在 Xcode 10 中,Swift 编译器足够智能:

  1. Hashable将包装值的Optionals视为 is Hashable

  2. Xcode >=9.4 也将struct包含所有Hashable 属性的 s 也视为Hashable.

奇怪的是,即使构建语言是 Swift 4,甚至是 Swift 3,Xcode 10 也将 Optionals 视为 Hashable。

以下面的代码为例:

struct Foo: Hashable {
    var string1: String?
    var string2: String?
}

该代码在 Xcode 10 下编译,并且使用 Foo 对象的 hashValue 可以按预期工作,即使构建语言是 Swift 3!

但是,在 Xcode 9.4 中,除非您将属性设为非可选,否则 Foo 结构不会自动 Hashable。

这段代码可以按照您在 Xcode 9.4/Swift 4.1 中的要求编译和工作:

struct Foo: Hashable {
    var string1: String
    var string2: String
}

如何Hashable在 Xcode 9 中创建选项?我当然可以自己为我的结构实现Hashable一致性,但是为多部分结构创建好的散列实际上有点棘手,我不想担心它。

我也想不出一种直接的方法来制作 Optional Hashable。(同样,这只是 Xcode < Xcode 10 中的一个问题。我的雇主还没有搬到 Xcode 10,所以我需要一个适用于 Xcode 9.4 的解决方案)

标签: swiftxcodeoptional

解决方案


Swift 4.2将Optional.swift中的条件Optional: Hashable一致性 定义 为

extension Optional: Hashable where Wrapped: Hashable {
  //  ...
  public func hash(into hasher: inout Hasher) {
    switch self {
    case .none:
      hasher.combine(0 as UInt8)
    case .some(let wrapped):
      hasher.combine(1 as UInt8)
      hasher.combine(wrapped)
    }
  }
}

尤其是

  • .none具有与整数零相同的哈希值,与类型无关,并且
  • 哈希值.some(wrapped)是通过将 的哈希值 wrapped与整数 1 的哈希值混合得到的。

Swift 4.1(Xcode 9.4 附带)已经拥有实现类似方法的必要工具:

  • 条件一致性,即我们可以定义一个扩展

    extension Optional: Hashable where Wrapped: Hashable
    
  • 如果所有成员都是 ,则自动合成Hashablefor 类型Hashable

这是一个可能的实现:

extension Optional: Hashable where Wrapped: Hashable {
    struct Combiner: Hashable {
        let left: Int
        let right: Wrapped
    }

    public var hashValue: Int {
        switch self {
        case .none:
            return 0.hashValue
        case .some(let wrapped):
            return Combiner(left: 1, right: wrapped).hashValue
        }
    }
}

例子:

struct Foo: Hashable {
    var string1: String?
    var string2: String?
}

let foo = Foo(string1: "1", string2: "2")
print(foo.hashValue) // 2171695307022640119

为了使这段代码也可以在 Swift 4.2 和更新版本中编译,可以有条件地编译扩展方法(比较 SE-0212 编译器版本指令):

#if swift(>=4.1.50) || (swift(>=3.4) && !swift(>=4.0))
// Code targeting the Swift 4.2 compiler and above:
// Conditional conformance `Optional: Hashable` defined in the standard library.
#elseif swift(>=4.1) || (swift(>=3.3) && !swift(>=4.0))
// Code targeting the Swift 4.1 compiler and above:

extension Optional: Hashable where Wrapped: Hashable {
    // ...
}

#endif

推荐阅读