Swift 标准资源库内置了很多通用协议,自定义的类型如果遵从 Swfit 通用的协议,将更加简单易用。
概览
使用自定义类型进行建模的时候, 有几种常见的场景:
- 检查两个值的是相同还是不同。
- 判断某个值是否包含在值列表中。
- 在集合(Set) 中存储值。
- 在字典(Dictionary) 中将值用作键。
这几个功能,由两个相关的标准资源协议 Equatable 和 Hashable 进行管理。
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生成了一个整数,可以用作索引。
协议的遵从
自动遵从协议
只需要在类型原始声明中声明遵从这些协议,便可以让许多自定类型成为 Equatable 和 Hashable 类型,而无需额外的代码。条件是:
- 如果是 struct 类型,其存储的所有属性都必须遵从
Equatable和Hashable。 - 如果是枚举,其关联的所有值,都必须遵从
Equatable和Hashable。
手动遵从协议
需要手动遵从协议的情况:
- 不满足上面能够自动遵从的条件。
- 你想要自定类型的遵从性。
- 你想要拓展在另一个文件或模块中声明的类型来实现遵从性。
代码示例:
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 类型关联的类型,都遵从Equatable和Hashable, 就可以自动遵从协议。
class 类型无法自动遵循协议。
跨文件,跨模块,也无法实现自动遵从协议。会报错,需要手动遵从协议。
注意事项
在实现 == 方法和 hash(into:) 方法时,应使用影响自定类型的两个实例是否被视为相等的所有属性。
如果类型中,包含不影响两个实例是否被视为相等的属性,可以从 == 方法和 hash(into:) 的哈希处理中排除这些属性。
务必要在 `==` 和 `hash(into:)` 方法中使用相同的属性。 如果在这两个方法中使用不同组的属性,那么在集合和字典中使用自定类型时,可能会出现异常行为或性能。
拓展
-
AnyHashable
-
特征运算符
===
用于判断两个类类型常量或者变量是否引用自相同的实例.
! 仅可用于类之间的判断
参考
原文:采用通用协议