确保自定类型遵从 Swift 协议,使它们更简单易用。
最新英文文章
概览
在程序中使用自定类型进行数据建模时,你可能经常需要检查两个值是相同还是不同,或者某个值是否包含在值列表中。这项功能以及在集合中储存值或在字典中将值用作键的功能由两个相关的标准资源库协议 Equatable
(英文) 和 Hashable
(英文) 进行管理。
-
你可以使用等于 (
==
) 和不等于 (!=
) 运算符来比较 Equatable 类型的不同实例。 -
Hashable 类型的实例可以将自己的值以数学方式缩减为单个整数,供集合和字典在内部使用以实现持续快速查询。
许多标准资源库类型都同时属于 Equatable 和 Hashable,包括字符串、整数、浮点值、布尔值,以及 Equatable 和 Hashable 类型的集合。以下示例中的 ==
比较和 contains(_:)
方法调用取决于字符串和整数都属于 Equatable:
遵从 Equatable
和 Hashable
协议非常简单明了,并让你可以更加轻松地在 Swift 中使用自定类型。因此建议你让所有的自定模型类别都遵从这两种协议。
自动遵从 Equatable 和 Hashable
只需在类型原始声明的同一文件中声明遵从这些协议,便可让许多自定类型成为 Equatable 和 Hashable 类型。在声明类型时将 Equatable
和 Hashable
添加到所采用协议的列表中后,编译器便会自动填充这两种协议的相关要求:
遵从 Equatable
后,你就能使用等于运算符 (==
) 或不等于运算符 (!=
) 来比较任意两个 Position
类型实例。
遵从 Hashable
意味着你可以将多个位置储存在一个集合中,并快速检查你以前是否到访过某个位置,如下例中所示:
除了简化你的代码外,这种自动遵从还能减少错误,因为在进行哈希和相等性测试时会自动包含你添加到自定类型中的所有新属性。只要类型是满足以下标准的结构或枚举,便符合自动遵从 Equatable
和 Hashable
的条件:
-
如果是结构,其储存的“所有”属性都必须遵从
Equatable
和Hashable
。 -
如果是枚举,其关联的“所有”值都必须遵从
Equatable
和Hashable
。(如果是无关联值的枚举,那么即使没有声明采用这些协议,也会遵从Equatable
和Hashable
。)
手动遵从 Equatable 和 Hashable
在以下情形中,你需要为类型手动实现 Equatable
和 Hashable
遵从性:
-
类型不满足之前一部分中列出的标准。
-
你想要自定类型的遵从性。
-
你想要扩展在另一个文件或模块中声明的类型来实现遵从性。
Player
类型是一个类,因此不符合自动合成 Equatable
或 Hashable
要求的条件。要让这个类遵从 Equatable
协议,请在扩展中声明遵从性并实现静态 ==
运算符方法。在 ==
方法的实现中比较各个重要属性的相等性:
要让 Player
遵从 Hashable
协议,请在另一个扩展中声明遵从性,并实现 hash(into:)
方法。在 hash(into:)
方法中,对提供的哈希计算器使用每个重要属性调用 combine(_:)
方法:
使用对 Equatable 和 Hashable 重要的所有属性
在实现 ==
方法和 hash(into:)
方法时,应使用影响自定类型的两个实例是否被视为相等的所有属性。在上面的实现中,Player
类型在两个方法中都使用了 name
和 position
。
如果你的类型包含不影响两个实例是否被视为相等的属性,请从 ==
方法的比较中和 hash(into:)
的哈希处理中排除这些属性。例如,某个类型可能缓存一个高开销的计算值,以便这个值只需计算一次。如果你要比较该类型的两个实例,那么该计算值缓存与否不应影响两个实例的相等性,因此应从比较和哈希处理中排除缓存的值。
重要信息
务必要在你的 ==
和 hash(into:)
方法中使用相同的属性。如果在这两个方法中使用不同组的属性,那么在集合和字典中使用自定类型时,可能会出现异常行为或性能。
自定 NSObject 子类行为
NSObject
子类会继承对 Equatable
和 Hashable
协议的遵从性,其相等性基于实例恒等性。如果需要自定此行为,应覆盖 is
(英文) 方法和 hash
(英文) 属性,而不是 ==
运算符方法和 hash
属性。
如前一部分中所述,被视为相等的两个实例必须具有相同的哈希值。如果你覆盖其中一个声明,那么也必须覆盖另一声明,从而维持该保证。