You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何让@objc协议遵循Hashable协议

解决@objc协议作为Swift字典键的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:41:44