Swift中如何让@objc协议遵循Hashable协议
你遇到的这个问题在Swift和Objective-C混编场景里挺常见的:Swift字典的键必须遵循Hashable协议,但@objc协议本身没法直接适配Swift的Hashable(毕竟Hashable不是Objective-C兼容的协议)。不过我们有几种靠谱的解决思路,你可以根据自己的需求来选:
方法一:让协议继承NSObjectProtocol(最省心的方案)
如果你的Document协议的遵守者都是NSObject的子类(这在混编项目里是很普遍的情况),直接让协议继承NSObjectProtocol就可以解决问题:
@objc public protocol Document: NSObjectProtocol { var data: Data? { get } var image: UIImage? { get } func generatePreview() }
为什么这能生效?因为所有NSObject的子类都会自动遵循Hashable——Swift会自动桥接Objective-C的hash方法和isEqual:方法,分别对应Hashable的哈希计算和相等性判断逻辑。默认情况下,它会基于对象的内存地址(指针)来判断相等性和计算哈希值,这对大多数场景来说足够用了。
方法二:自定义Hashable实现(灵活可控)
如果不想让协议依赖NSObjectProtocol,或者需要自定义相等/哈希逻辑,可以按照下面的步骤来操作:
1. 给协议添加@objc兼容的hash属性
先修改协议,添加Objective-C原生支持的hash属性(所有Objective-C对象本身就自带这个属性):
@objc public protocol Document: class { var data: Data? { get } var image: UIImage? { get } func generatePreview() @objc var hash: Int { get } }
2. 在Swift扩展中实现Hashable协议
接着给Document写一个Swift扩展,实现Hashable的要求:
extension Document: Hashable { // 把@objc的hash值传入Hasher完成哈希计算 public func hash(into hasher: inout Hasher) { hasher.combine(hash) } // 默认用对象身份判断相等(和NSObject默认行为一致) public static func == (lhs: Document, rhs: Document) -> Bool { return lhs === rhs } }
如果你需要自定义相等逻辑(比如根据data或者image的内容判断两个Document实例是否相等),可以修改==方法,但一定要保证相等的实例必须有相同的hash值,否则字典会出现查找错误。举个例子:
public static func == (lhs: Document, rhs: Document) -> Bool { return lhs.data == rhs.data && lhs.image == rhs.image }
对应的,hash(into:)也要改成基于这些属性计算:
public func hash(into hasher: inout Hasher) { hasher.combine(data) hasher.combine(image) }
注意事项
- 因为
@objc协议只能被类遵守,所以用===判断对象身份是安全的(值类型没法遵守@objc协议)。 - 自定义相等逻辑时,务必保证哈希值和相等逻辑的一致性——这是
Hashable协议的核心要求,违反它会导致字典、集合等容器出现不可预测的行为。
内容的提问来源于stack exchange,提问作者kikettas

