简体   繁体   English

Swift 如何在协议中符合不同的关联类型

[英]Swift how to conform to different associate type in a protocol

I am developing a state management library.我正在开发一个 state 管理库。 The original design only has 1 listener, which works great until I need to support multiple listeners.最初的设计只有 1 个监听器,在我需要支持多个监听器之前效果很好。

The original design is here: Swift how to use generic protocol in generic class原始设计在这里: Swift how to use generic protocol in generic class

This is what I have done to support multiple listeners:这是我为支持多个听众所做的:

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

public final class StateStore<Observer: StateObserver> {

  struct WeakRef<T: AnyObject> {
    weak var value: T?
  }

  public private(set) var state: Observer.State
  private var observers = [WeakRef<Observer>]()
  
  public init(initialState: Observer.State) {
    state = initialState
  }
  
  public func addObservers(_ observers: [Observer]) {
    self.observers += observers.map { WeakRef(value: $0) }
  }
  
  public func update(_ block: (inout Observer.State) -> Void) {
    var nextState = state
    block(&nextState)
    state = nextState
    notify()
  }
  
  public func notify() {
    for observer in observers {
      observer.value?.didUpdateState(state)
    }
  }
}

Now I need to create the store with 2 observers:现在我需要用 2 个观察者创建商店:

class MyScene: SKScene {
  init {
    let leftPanel = LeftPanelSKNode()
    let topBar = TopBarSKNode()
    let store: StateStore<?> // How to make this support `LeftPanelSKNode `, `TopBarSKNode`, and `MyScene`? 
    store.addObservers([leftPanel, topBar, self])
  }

Now I am stuck here.现在我被困在这里。 I need to create a StateStore<?> of something, which can be either MyScene , LeftPanelSKNode and TopBarSKNode .我需要创建一个 StateStore<?> 的东西,它可以是MySceneLeftPanelSKNodeTopBarSKNode

First of all, I have to say that what you are building already exists in many reactive libraries:首先,我不得不说您正在构建的内容已经存在于许多反应式库中:

CurrentValueSubject in Apple's Combine; Apple 组合中的CurrentValueSubject

BehaviorSubject in RxSwift; RxSwift 中的BehaviorSubject

You can also check the small internal class I've made myself, it allows to hold the state and observe it ObservableProperty .您还可以检查我自己制作的小型内部 class,它允许握住 state 并观察它ObservableProperty

Back to your question, I've found a way to add the StateObserver one by one while keeping only the weak reference to them.回到您的问题,我找到了一种方法来逐个添加 StateObserver,同时只保留对它们的弱引用。

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

class Node1: StateObserver {
    typealias State = Int

    func didUpdateState(_ state: Int) { }
}

class Node2: StateObserver {
    typealias State = Int

    func didUpdateState(_ state: Int) { }
}

class StateStore<StateType> {
    private(set) var state: StateType

    init(_ initialState: StateType) {
        self.state = initialState
    }

    private var observers: [(StateType) -> Void] = []

    func observe<Observer: StateObserver>(by observer: Observer) where Observer.State == StateType {
        weak var weakObserver = observer

        observers.append { state in
            weakObserver?.didUpdateState(state)
        }
    }

    func notify() {
        observers.forEach {
            $0(self.state)
        }
    }
}

let store = StateStore<Int>(0)

let node1 = Node1()
let node2 = Node2()

store.observe(by: node1)
store.observe(by: node2)

Adding the array-based observe API might be a problem because of the associatedtype in the StateObserver.由于 StateObserver 中的关联类型,添加基于数组的观察 API 可能会出现问题。

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

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