协议 Equatable & Hashable

294 阅读3分钟

Swift 标准资源库内置了很多通用协议,自定义的类型如果遵从 Swfit 通用的协议,将更加简单易用。


概览

使用自定义类型进行建模的时候, 有几种常见的场景:

  • 检查两个值的是相同还是不同。
  • 判断某个值是否包含在值列表中。
  • 集合(Set) 中存储值。
  • 字典(Dictionary) 中将值用作键。

这几个功能,由两个相关的标准资源协议 EquatableHashable 进行管理。


Equatable & Hashable

Equatable

允许使用 ==!= 运算符来比较 Equatable 类型的不同实例。

相关代码:

// 检查两个值的是相同还是不同.
if username == "Arturo" {
    print("相同")
}
// 判断某个值是否包含在值列表中.
if numberArray.contains(number) {
    print("包含")
}

Hashable

可以将自己的值以数学的方式缩减为单个整数,供 集合字典 在内部使用,以实现快速查询。

相关代码:

// 在集合(Set)中存储值.
let numberSet: Set = [1, 2, 3]
if numberSet.contains(number) {
    print("包含")
}
// 在字典(Dictionary)中将值用作键.
let dic = [number: "1"]

稍作总结
Equatable 用于判断值是否相同。
Hashable 生成了一个整数,可以用作索引。


协议的遵从

自动遵从协议

只需要在类型原始声明中声明遵从这些协议,便可以让许多自定类型成为 EquatableHashable 类型,而无需额外的代码。条件是:

  • 如果是 struct 类型,其存储的所有属性都必须遵从EquatableHashable
  • 如果是枚举,其关联的所有值,都必须遵从 EquatableHashable

手动遵从协议

需要手动遵从协议的情况:

  • 不满足上面能够自动遵从的条件。
  • 你想要自定类型的遵从性。
  • 你想要拓展在另一个文件或模块中声明的类型来实现遵从性。

代码示例:

extension Player: Equatable {
    static func ==(lhs: Player, rhs: Player) -> Bool {
        return lhs.name == rhs.name && lhs.position == rhs.position
    }
}

extension Player: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(name)
        hasher.combine(position)
    }
}

稍作总结
struct & enum 类型关联的类型,都遵从 EquatableHashable, 就可以自动遵从协议。
class 类型无法自动遵循协议。
跨文件,跨模块,也无法实现自动遵从协议。会报错,需要手动遵从协议。


注意事项

在实现 == 方法和 hash(into:) 方法时,应使用影响自定类型的两个实例是否被视为相等的所有属性。
如果类型中,包含不影响两个实例是否被视为相等的属性,可以从 == 方法和 hash(into:) 的哈希处理中排除这些属性。

务必要在 `==` 和 `hash(into:)` 方法中使用相同的属性。 如果在这两个方法中使用不同组的属性,那么在集合和字典中使用自定类型时,可能会出现异常行为或性能。


拓展

  • AnyHashable

  • 特征运算符 ===
    用于判断两个类类型 常量或者变量是否引用自相同的实例.
    ! 仅可用于类之间的判断


参考

原文:采用通用协议