繁体   English   中英

Swift 枚举符合可识别:类型不符合可识别协议

[英]Swift enum conformance to identifiable: Type doesn't conform to Identifiable protocol

我有一个具有关联值的枚举,我想将其用作 RxDataSources 中的一个项目。 我尝试通过如下所示使其符合 Hashable 来使其符合可识别

enum DriverHubWidget: Hashable, Identifiable {
    static func == (lhs: DriverHubWidget, rhs: DriverHubWidget) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }
    
    var id: Int { hashValue }
    
    case greetings(DriverHubGreetingsViewModel)
    case scorecard(DriverHubScorecardSummary?, Error?)
    case optOut
    
    func hash(into hasher: inout Hasher) {
        switch self {
        case .greetings( _):
            return hasher.combine(1)
        case .scorecard( _, _):
            return hasher.combine(2)
        case .optOut:
            return hasher.combine(3)
        }
    }
}

我通过简单地为每个案例分配一个 Int 值来实现哈希 function。 然后为了符合可识别性,我添加了一个返回哈希值的 id 属性。 这编译得很好。

现在,当我尝试使用它为 model 部分声明类型别名时,如下所示

typealias WidgetSection = AnimatableSectionModel<String, DriverHubWidget>

它确实编译并抛出错误, Type 'DriverHubWidget' does not conform to protocol 'IdentifiableType'

我不明白为什么它不起作用,当枚举符合 Hashable 和 Identifiable 时它编译得很好,但是当使用时一致性不知何故无效是因为枚举的关联值不是 Hashable 吗?

您将 Swift 内置协议Identifiable与 RxDataSource 库中的协议IdentifiableType混淆了。

您可以只符合IdentifiableType

enum DriverHubWidget: Hashable, IdentifiableType {
    
    var identity: Int {
        hashValue
    }

    ...
}

不过,您遵守Hashable的方式对我来说似乎很奇怪。 您认为枚举的两个值只要大小写相同就相等,而忽略它们的关联值。 也就是说, .greeting(x) ==.greeting(y)为真。 这似乎相当违反直觉。 如果这真的是您想要的identity ,您可能只想以这种方式实现identity

var identity: Int {
    switch self {
    case .greetings( _):
        return 1
    case .scorecard( _, _):
        return 2
    case .optOut:
        return 3
    }
}

并通过实际考虑关联值来符合Hashable ,或者根本不符合Hashable

(这不是一个完整的答案,但评论太长了。将其视为 Sweeper 已经说过的内容的附录)

对于可Identifiable的对象,它们需要有一个稳定的(即不随时间改变)身份概念,以将它们与其他相关对象区分开来。 究竟哪种身份概念对您的目的有意义取决于您。 正如文档中提到的

Identifiable 未指定身份的持续时间和 scope。

  • 身份可以具有以下任何特征: 保证始终唯一,如 UUID。
  • 每个环境持久唯一,如数据库记录键。
  • 在进程的生命周期内是唯一的,例如全局递增整数。
  • 在 object 的生命周期内是唯一的,例如 object 标识符。
  • 在当前集合中是唯一的,如集合索引。 记录身份的性质取决于协议的符合者和接受者。
  1. 您可能不想忽略身份概念中的相关值。 否则,您的代码可能会尝试将两个对象视为相同,即使它们的关联值不同。

    实际上,这意味着DriverHubGreetingsViewModelDriverHubScorecardSummary也需要符合Identifiable 你的Error? 关联值,您可能想要制作成(Error & Identifiable)? *

  2. 您不能将id的实现细化为hashValue ,因为 hash 值(按设计)是不可预测的。 您的所有三个案例完全有可能以相同的 id 结束(如果散列的播种方式是 1、2 和 3 的散列都发生冲突,则会发生这种情况)

另一个注意事项:有一个可选的 model,后面跟着一个可选的错误是 Swift 中的代码味道。这是从目标 C 中保留下来的,其类型系统缺乏一种轻量级的方式来表达一种另一种类型的值(“或”或“求和”类型)。 Swift 支持具有关联值的枚举(您已经在使用一个。)它甚至可以是通用的:标准库中已经为您内置了一个: Result<Success, Failure>

因此,而不是case scorecard(DriverHubScorecardSummary?, Error?) ,我建议:

case scorecard(Result<DriverHubScorecardSummary, Error & Identifiable>)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM