此源码解析适用于
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(¤tState, 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
}
}