Swift 如何在泛型类中使用泛型协议

时间:2021-03-23 07:19:44

标签: ios swift

我正在编写一个用于状态管理的库。它基本上是一种简化的观察者模式,只有 1 个观察者/侦听器。

现在我有这个并且效果很好:

public final class StateStore<S> {
  
  private var currentState: S
  
  public var listener: ((S) -> Void)? = nil
  
  public init(initialState: S) {
    currentState = initialState
  }
  
  func update(_ block: (inout S) -> Void) {
    var nextState = currentState // struct's copy on write
    block(&nextState)
    currentState = nextState
    listener?(currentState)
  }
  
}

但是,我想将其更改为协议而不是块。类似的东西:

public protocol StateListener: AnyObject {
  associatedtype S
  func didUpdateState(_ state: S)
}

public final class StateStore<S> {
  ...
  public weak var listener: StateListener<S>? // <- how to deal with associate type 
  ... 
}

我不能这样做,因为在上面的 S 是关联类型,而不是泛型类型。所以我收到错误说 Cannot specialize non-generic type 'StateListener'

我看过这个但没有帮助: Using generic protocols in generic classes

2 个答案:

答案 0 :(得分:2)

S 不是 Swift 风格;使用完整的单词是现代标准。

public protocol StateListener: AnyObject {
  associatedtype State
  func didUpdateState(_: State)
}

public final class StateStore<Listener: StateListener> {
  public weak var listener: Listener?
}

您无需手动考虑关联类型。它是内置的,可作为嵌套类型访问:

  func ƒ(_: Listener.State) {

  }

答案 1 :(得分:1)

关键是将 StateStore 参数化为 Listener 类型,而不是 S 类型。 S 然后可以定义为 Listener.S 的类型别名:

public final class StateStore<Listener: StateListener> {
    public typealias S = Listener.S

请注意,现在您不能拥有 StateStore<StateListener> 类型的变量,它可以存储具有不同 StateStore 实现的所有类型的 StateListenerStateStore<StateListener> 类型没有意义,and for good reason too

我想说,想把它变成一个协议是很奇怪的。如果您想给 (S) -> Void 类型一个 name,请在 StateStore 中使用类型别名:

public typealias StateListener = ((S) -> Void)