【The Composable Architecture (TCA) 源码解析】08 - Store

660 阅读4分钟

此源码解析适用于 0.41.0 之前的版本。最新的源码已经把 Reducer 重构成了一个协议 ReducerProtocol,但核心的实现方法还是离不开这个版本的源码。

Store 驱动着整个应用程序,它被传入与应用程序交互的 views 里面。例如:

@main
struct MyApp: App {
  var body: some Scene {
    WindowGroup {
      RootView(
        store: Store(
          initialState: AppState(),
          reducer: appReducer,
          environment: AppEnvironment(
            ...
          )
        )
      )
    }
  }
}

Store 不是线程安全的,因此与 Store 的所有交互必须在同一线程(创建时的线程)上完成。此外,如果 Store 用于驱动 SwiftUI 或 UIKit,那么所有交互都必须在主线程上完成。

Store 不是线程安全的原因是,当 Action 被发送到 Store 时,Reducer 是在当前 state 下运行的,这个过程不能在多个线程中完成。通过引入锁或队列可以使此进程线程安全,但这引入了新的复杂性:

  • 如果简单使用 DispatchQueue.main.async 也将会引发线程跳变,即使在已经在主线程上。这可能导致 UIKit 和 SwiftUI 中出现意外行为,其中有时需要同步执行工作,例如在动画块中。
  • 可以创建一个调度器 (scheduler),在主线程上立即执行其工作,否则使用 DispatchQueue.main。async

这引入了更多的复杂性,如果没有非常好的理由,不应该采用。

public final class Store<State, Action> {
  // 存储未执行的 `Action`
  private var bufferedActions: [Action] = []

  // 在 `send` 方法中,当 Effect.operation 是 `.publisher` 时,存储订阅 Publisher 时返回的 `AnyCancellable`
  var effectCancellables: [UUID: AnyCancellable] = [:]

  // 是否正在处理某个 `Action`,避免一个 `Action` 没处理完成又处理另外一个
  private var isSending = false

  // 存储订阅 parent 的 `state` 时返回的 `AnyCancellable`,具体查看 scope 相关代码
  var parentCancellable: AnyCancellable?

  // 存储 reducer
  private let reducer: (inout State, Action) -> Effect<Action, Never>

  // 存储用于处理 Scope 的对象,具体查看 scope 相关代码
  fileprivate var scope: AnyScope?

  // 当前的 `State`
  var state: CurrentValueSubject<State, Never>

  // 是否检查主线程
  #if DEBUG
    private let mainThreadChecksEnabled: Bool
  #endif

  // 创建 Store
  public convenience init<Environment>(
    initialState: State,
    reducer: Reducer<State, Action, Environment>,
    environment: Environment
  ) {
    self.init(
      initialState: initialState,
      reducer: reducer,
      environment: environment,
      mainThreadChecksEnabled: true
    )
    self.threadCheck(status: .`init`)
  }

  // 创建 Store
  init<Environment>(
    initialState: State,
    reducer: Reducer<State, Action, Environment>,
    environment: Environment,
    mainThreadChecksEnabled: Bool
  ) {
    self.state = CurrentValueSubject(initialState)
    self.reducer = { state, action in reducer.run(&state, action, environment) }

    #if DEBUG
      self.mainThreadChecksEnabled = mainThreadChecksEnabled
    #endif
  }

  // 将 `Store` 的范围限定为只暴露 child state 和 actions。
  // 这对于在应用程序中派生新的 `Store` 以传递给 child views 非常有用。例如:
  /// ```swift
  /// // Application state made from child states.
  /// struct AppState { var login: LoginState, ... }
  /// enum AppAction { case login(LoginAction), ... }
  ///
  /// // A store that runs the entire application.
  /// let store = Store(
  ///   initialState: AppState(),
  ///   reducer: appReducer,
  ///   environment: AppEnvironment()
  /// )
  ///
  /// // Construct a login view by scoping the store to one that works with only login domain.
  /// LoginView(
  ///   store: store.scope(
  ///     state: \.login,
  ///     action: AppAction.login
  ///   )
  /// )
  /// ```
  // 以这种方式定义范围可以让我们更好地模块化应用程序。在这种情况下,可以将 `LoginView` 提取到无法访问 `AppState ` 或 `AppAction` 的模块。
  public func scope<ChildState, ChildAction>(
    state toChildState: @escaping (State) -> ChildState,
    action fromChildAction: @escaping (ChildAction) -> Action
  ) -> Store<ChildState, ChildAction> {
    self.threadCheck(status: .scope)
    // 具体实现请看 `Scope`
    return (self.scope ?? Scope(root: self))
      .rescope(self, state: toChildState, action: fromChildAction)
  }

  // 将 `Store` 的范围限定为只暴露 child state。
  public func scope<ChildState>(
    state toChildState: @escaping (State) -> ChildState
  ) -> Store<ChildState, Action> {
    self.scope(state: toChildState, action: { $0 })
  }

  // 发送 `Action`。这是 `Store` 类型中最复杂也是最重要的方法
  func send(
    _ action: Action,
    originatingFrom originatingAction: Action? = nil
  ) -> Task<Void, Never>? {
    self.threadCheck(status: .send(action, originatingAction: originatingAction))

    // 先把 `action` 缓存起来
    self.bufferedActions.append(action)
    // 如果正在执行,提前返回
    guard !self.isSending else { return nil }

    // 正在执行,设置为 `true`
    self.isSending = true
    // 执行 `action` 前的 state,后面被传入 `reducer` 中修改
    var currentState = self.state.value
    // 用于缓存执行 `action` 时返回的 Effect 转化而来的任务,
    // 这里使用 `Box` 的原因是 Array 是值类型,这里需要引用类型
    let tasks = Box<[Task<Void, Never>]>(wrappedValue: [])

    // 整个方法运行结束后执行的一些操作
    defer {
      // `withExtendedLifetime` 的作用是确保 `self.bufferedActions` 在闭包执行完成前不会被销毁。
      // TODO: 至于为什么要做这样一个操作,暂时还不知道。
      withExtendedLifetime(self.bufferedActions) {
        self.bufferedActions.removeAll()
      }
      // 所有缓存的 actions 执行完成后,`currentState` 已被完成修改,更新 state 的值
      self.state.value = currentState
      // 执行结束,设置为 `false`
      self.isSending = false

      // 如果缓存的 actions 还不为空,使用递归,继续执行
      if !self.bufferedActions.isEmpty {
        if let task = self.send(
          self.bufferedActions.removeLast(), originatingFrom: originatingAction
        ) {
          tasks.wrappedValue.append(task)
        }
      }
    }

    // 循环执行缓存的 actions
    var index = self.bufferedActions.startIndex
    while index < self.bufferedActions.endIndex {
      // 完成一次循环,index 递增
      defer { index += 1 }
      let action = self.bufferedActions[index]
      // 执行 `reducer`,拿到 effect
      let effect = self.reducer(&currentState, action)

      // 根据不同的 `operation`,把 effect 转换成 Task
      switch effect.operation {
      case .none:
        break
      case let .publisher(publisher): // `operation` 是 `.publisher` 类型
        // `publisher` 是否完成
        var didComplete = false
        let boxedTask = Box<Task<Void, Never>?>(wrappedValue: nil)
        let uuid = UUID()
        // 订阅 `publisher` 的变化
        let effectCancellable =
          publisher
          .handleEvents(
            receiveCancel: { [weak self] in
              self?.threadCheck(status: .effectCompletion(action))
              // 取消订阅,把 uuid 对应的 cancellable 设置为 nil
              self?.effectCancellables[uuid] = nil
            }
          )
          .sink(
            receiveCompletion: { [weak self] _ in
              self?.threadCheck(status: .effectCompletion(action))
              // `publisher` 完成,要把关联的 `Task` 取消
              boxedTask.wrappedValue?.cancel()
              didComplete = true
              // 把 uuid 对应的 cancellable 设置为 nil
              self?.effectCancellables[uuid] = nil
            },
            receiveValue: { [weak self] effectAction in
              guard let self = self else { return }
              // 接收到 action,使用递归执行 action,并把返回的 `Task` 存到数组中
              if let task = self.send(effectAction, originatingFrom: action) {
                tasks.wrappedValue.append(task)
              }
            }
          )

        // 如果 `publisher` 没有完成
        if !didComplete {
          // 使用 `Task` 等待 `publisher` 完成
          let task = Task<Void, Never> { @MainActor in
             // 如果 `task` 没有被取消,代码会一直停在这里,直到取消为止
            for await _ in AsyncStream<Void>.never {}
            // `task` 取消后,把 cancellable 也取消
            effectCancellable.cancel()
          }
          boxedTask.wrappedValue = task
          tasks.wrappedValue.append(task)
          self.effectCancellables[uuid] = effectCancellable
        }
      case let .run(priority, operation): // `operation` 是 `.run` 类型
        // 直接把 `Task` 添加到数组中
        tasks.wrappedValue.append(
          Task(priority: priority) {
            await operation(
              Send {
                // 使用递归执行 action,并把返回的 `Task` 存到数组中
                if let task = self.send($0, originatingFrom: action) {
                  tasks.wrappedValue.append(task)
                }
              }
            )
          }
        )
      }
    }

    // 至此,已经把执行所有缓存的 actions 返回的 Effect 转化成任务,
    // 如果任务为空,提前返回
    guard !tasks.wrappedValue.isEmpty else { return nil }

    // 任务不为空,返回一个全新的 `Task`,工作是按顺序执行 `tasks`
    return Task {
      await withTaskCancellationHandler {
        // 如果全新的 `Task` 被取消,那么也把 `tasks` 取消
        var index = tasks.wrappedValue.startIndex
        while index < tasks.wrappedValue.endIndex {
          defer { index += 1 }
          tasks.wrappedValue[index].cancel()
        }
      } operation: {
        // 顺序执行 `tasks`
        var index = tasks.wrappedValue.startIndex
        while index < tasks.wrappedValue.endIndex {
          defer { index += 1 }
          await tasks.wrappedValue[index].value
        }
      }
    }
  }

  /// 返回不暴露 state 的 Store
  public var stateless: Store<Void, Action> {
    self.scope(state: { _ in () })
  }

  /// 返回不暴露 actions 的 Store
  public var actionless: Store<State, Never> {
    func absurd<A>(_ never: Never) -> A {}
    return self.scope(state: { $0 }, action: absurd)
  }
}

// `Scope` 要遵循的协议
private protocol AnyScope {
  func rescope<ScopedState, ScopedAction, RescopedState, RescopedAction>(
    _ store: Store<ScopedState, ScopedAction>,
    state toRescopedState: @escaping (ScopedState) -> RescopedState,
    action fromRescopedAction: @escaping (RescopedAction) -> ScopedAction
  ) -> Store<RescopedState, RescopedAction>
}

private struct Scope<RootState, RootAction>: AnyScope {
  // 当前 `Scope` 对象的 root `Store`
  let root: Store<RootState, RootAction>

  // 一个闭包:将限定范围的 `Store` 的 action 转换为 root `Store` 的 action。
  // 定义为 `Any` 类型的原因是需要支持 `ScopedAction` 泛型
  let fromScopedAction: Any

  init(root: Store<RootState, RootAction>) {
    self.init(root: root, fromScopedAction: { $0 })
  }

  private init<ScopedAction>(
    root: Store<RootState, RootAction>,
    fromScopedAction: @escaping (ScopedAction) -> RootAction
  ) {
    self.root = root
    self.fromScopedAction = fromScopedAction
  }

  func rescope<ScopedState, ScopedAction, RescopedState, RescopedAction>(
    _ scopedStore: Store<ScopedState, ScopedAction>,
    state toRescopedState: @escaping (ScopedState) -> RescopedState,
    action fromRescopedAction: @escaping (RescopedAction) -> ScopedAction
  ) -> Store<RescopedState, RescopedAction> {
    // 强制转换,得到具体类型
    let fromScopedAction = self.fromScopedAction as! (ScopedAction) -> RootAction

    // 是否正在执行 reducer 的标志
    var isSending = false

    // 创建限定范围的 Store
    let rescopedStore = Store<RescopedState, RescopedAction>(
      initialState: toRescopedState(scopedStore.state.value), // 限定范围的初始值
      reducer: .init { rescopedState, rescopedAction, _ in
        // 正在执行 reducer,`isSending` 设为 `true`
        isSending = true
        // 最后完成 reducer 执行,`isSending` 设为 `false`
        defer { isSending = false }

        // 调用 `self.root.send` 去实际执行 reducer。假设有以下 actions:
	// ```swift
	// enum RootAction {
	//   case actionA(ChildActionA)
	// }
	// enum ChildActionA {
	//   case actionB(ChildActionB)
	// }
	// enum ChildActionB {
	//   case actionC
        // }
        // ```
        // 与 `self.root` 关联的是 `RootAction`,`rescopedAction` 是 `.actionC `,
        // 它的参数包了很多层,目的是将 `.actionC` 转换成 `.actionA`。
        let task = self.root.send(fromScopedAction(fromRescopedAction(rescopedAction)))

        // 上一行代码 send action 成功,与 `self.root` 关联的 state 会更新,所以这里同时更新限定范围的 state
        rescopedState = toRescopedState(scopedStore.state.value)
        if let task = task {
          // 返回 `Effect`,它的工作是等待 `task` 完成
          return .fireAndForget { await task.cancellableValue }
        } else {
          return .none
        }
      },
      environment: ()
    )

    // 订阅上一级的 state 的变化
    rescopedStore.parentCancellable = scopedStore.state
      .dropFirst() // 因为 `scopedStore.state` 是 `CurrentValueSubject` 类型,订阅的时候就会发出当前的值,所以忽略掉第一个
      .sink { [weak rescopedStore] newValue in
        // 在 `isSending` 为 `false` 时才去更新限定范围的 state;如果为 `true`, 说明 reducer 正在执行,不需要在订阅这里更新
        guard !isSending else { return }
        rescopedStore?.state.value = toRescopedState(newValue)
      }

    // 为 `scope` 赋值,`rescopedStore` 对应的范围 `Scope`
    rescopedStore.scope = Scope<RootState, RootAction>(
      root: self.root,
      fromScopedAction: { fromScopedAction(fromRescopedAction($0)) }
    )
    return rescopedStore
  }
}