ReSwift 版本演进全解析:从 0.2.4 到 6.2.0 的 API 变迁与技术原理

发布时间:2026/9/27 21:14:36
ReSwift 版本演进全解析:从 0.2.4 到 6.2.0 的 API 变迁与技术原理 【免费下载链接】ReSwiftUnidirectional Data Flow in Swift - Inspired by Redux项目地址https://gitcode.com/gh_mirrors/re/ReSwift点击查看免费下载ReSwift 是一个受 Redux 启发的 Swift 单向数据流Unidirectional Data Flow状态管理库。本文以仓库 CHANGELOG.md 为主线梳理 ReSwift 从 0.2.4 到 6.2.0 的完整演进历史深入讲解 Subscription 订阅 API、Reducer 函数化、Middleware 泛型化、automaticallySkipsRepeats去重机制等关键变革的来龙去脉并结合 ReSwift/CoreTypes 下的源码实现与 ReSwiftTests 中的测试用例说明每一项 API 变迁背后的设计动机与底层原理。读完本文你将掌握 ReSwift 各版本的核心差异、迁移要点以及订阅、Reducer、Middleware、Store 这几个核心组件当前版本的真实工作方式。一、版本演进总览ReSwift 的发布历史集中记录了从 2016 年初到 2023 年初的七年间项目从快速迭代走向稳定成熟的过程版本发布日期类型里程碑事件0.2.42016-01-23早期支持 reducer 为 Store 提供初始状态0.2.52016-02-20早期引入订阅子状态选择、ReSwiftInit、订阅弱引用1.0.02016-03-19正式版移除同步 dispatch 回调参数文档迁移至 Docs2.0.02016-06-27大版本StandardAction序列化代码重大改进2.1.02016-09-15兼容Swift 3 预览兼容、测试迁移至 XCTest3.0.02016-11-12大版本支持 Swift 3Store恢复为open4.0.02017-04-19大版本全新 Subscription API、Reducer 函数化4.0.12017-12-19修复修复订阅内存管理与 skipRepeats 缺陷4.1.02019-03-21功能弃用 StandardAction / ActionCreator、新增automaticallySkipsRepeats4.1.12019-03-21修复修复自动去重在非 Equatable 根状态下的回归5.0.02019-06-30大版本彻底移除 StandardAction、state 改为private(set)6.0.02020-10-01大版本支持 Swift 5、Middleware 可后置变更6.1.02021-05-09功能弃用StateType协议、修复泛型订阅去重 bug6.1.12023-01-06修复扩展方法open func改为public func6.2.0—大版本移除旧部署目标最低支持 macOS 10.13 / iOS 12 / tvOS 12 / watchOS 4从演进轨迹可以清晰地看到两条主线一方面 API 持续收敛简化StateType、StandardAction、ActionCreator 等历史包袱被逐步移除或迁移另一方面订阅与去重机制不断强化从手动skipRepeats到默认自动去重。下文将按主题深入剖析。二、Subscription 订阅 API4.0.0 的分水岭2.1 旧版3.0.0的痛点在 4.0.0 之前订阅子状态的能力非常原始用户通过传入一个返回元组的闭包来挑选感兴趣的状态// ReSwift 3.0.0 的订阅方式 store.subscribe(subscriber) { ($0.testValue, $0.otherState?.name) }这种写法的问题在于订阅行为完全耦合在闭包里无法对选择的状态做进一步加工也无法表达什么情况下才通知我这类过滤逻辑。2.2 新版 Subscription API 与操作符4.0.0 引入的全新 Subscription API由 #203 提出Ben-G、mjarvis、DivineDominion 共同完成将订阅拆分为**选择select与去重skipRepeats**两个正交维度// ReSwift 4.0.0 的订阅方式select 闭包选择子状态 store.subscribe(subscriber) { $0.select { ($0.testValue, $0.otherState?.name) } } // 非 Equatable 子状态用闭包自定义去重规则 store.subscribe(subscriber) { $0.select { $0.testValue }.skipRepeats { return $0 $1 } }这套 API 的本质在 Subscription.swift 的源码中体现得非常清晰Subscription被设计成一个轻量级信号/可观察对象signal/observable。select通过_select内部方法把选择器闭包包装成一个新的订阅当原始订阅收到新旧状态时先经选择器映射、再转发给下游private func _selectSubstate( _ selector: escaping (State) - Substate ) - SubscriptionSubstate { return SubscriptionSubstate { sink in self.observer { oldState, newState in sink(oldState.map(selector) ?? nil, selector(newState)) } } }skipRepeats(_:)则在转发前比较新旧状态判定为重复更新时直接丢弃见 Subscription.swift。这正是从源码层面印证了 CHANGELOG 所描述的skip calls tonewStateunless state value changed。2.3 后续增强key path、skip(when:)、only(when:)6.0.0为select增加了 KeyPath 重载#415现在可以写出store.subscribe(self) { $0.select(\.name) }这种更 Swift 惯用的写法实现见 Subscription.swift。4.1.0增加了skip(when:)与only(when:)两个便捷方法#242。skip(when:)与skipRepeats语义相同而only(when:)是其逻辑取反——只在条件满足时才通知订阅者。其实现非常精妙only内部直接复用skipRepeats只是把判定闭包取反见 Subscription.swiftpublic func only(when: escaping (_ oldState: State, _ newState: State) - Bool) - SubscriptionState { return self.skipRepeats { oldState, newState in return !when(oldState, newState) } }4.1.0将SubscriptionState打开为open#383允许外部库扩展订阅能力。2.4 订阅的默认去重automaticallySkipsRepeats4.1.0在 Store 初始化器中新增automaticallySkipsRepeats配置项#262默认值为true。该选项的源码实现在 Store.swiftStore 构造时把它保存为subscriptionsAutomaticallySkipRepeats随后在 Store.swift 的 Equatable 泛型订阅扩展中生效extension Store { public func subscribeSelectedState: Equatable, S: StoreSubscriber( _ subscriber: S, transform: ((SubscriptionState) - SubscriptionSelectedState)? ) where S.StoreSubscriberStateType SelectedState { let originalSubscription SubscriptionState() var transformedSubscription transform?(originalSubscription) if subscriptionsAutomaticallySkipRepeats { transformedSubscription transformedSubscription?.skipRepeats() } _subscribe(subscriber, originalSubscription: originalSubscription, transformedSubscription: transformedSubscription) } }也就是说只要订阅的子状态实现了EquatableStore 就会自动为你应用skipRepeats()避免无关状态变化导致 UI 频繁刷新。这一机制有专门的测试覆盖见 AutomaticallySkipRepeatsTests.swift例如testDispatchUnrelatedActionWithoutExplicitSkipRepeatsWithRegularSubstateSelection验证了订阅$0.name后派发修改age的 action订阅回调次数仍保持为 1。值得注意的版本细节4.1.1修复了 4.1.0 的一个回归#399当根状态不是 Equatable 但选中的子状态是 Equatable 时自动去重失效本次修复确保自动去重正确生效。6.1.0修复了在泛型S: StoreType上订阅时automaticallySkipsRepeats被忽略的 bug#463。若想关闭自动去重只需在初始化 Store 时传automaticallySkipsRepeats: false对应 API 变更见 #262。三、Reducer从类型到函数3.1 4.0.0 的破坏性变更4.0.0 之前Reducer 是一个需要实现handleAction方法的类型4.0.0 起#177Reducer 类型被移除改为纯函数。CHANGELOG 给出了新式 reducer 的完整示例func counterReducer(action: Action, state: AppState?) - AppState { var state state ?? AppState() switch action { case _ as CounterActionIncrease: state.counter 1 case _ as CounterActionDecrease: state.counter - 1 default: break } return state }当前源码中Reducer 被定义为一个简单的类型别名见 Reducer.swiftpublic typealias ReducerReducerStateType (_ action: Action, _ state: ReducerStateType?) - ReducerStateType注意这里的关键设计state 参数是可选类型ReducerStateType?。这意味着 reducer 既可以基于已有状态计算新状态也可以在状态尚不存在时用默认值水合hydrateStore。这个能力最早在0.2.4引入——Store 可以以空状态初始化由 reducer 负责提供初始状态#61 相关变更。3.2 初始状态的两种来源结合 Store.swift 可以看到 Store 初始化时对两种初始状态来源的处理if let state state { self.state state } else { dispatch(ReSwiftInit()) }显式传入state直接作为初始状态传入nilStore 会自动派发ReSwiftInitaction让 reducer 借此机会构造并返回初始状态。ReSwiftInit定义在 Action.swift它是在0.2.5中由原来的匿名初始化 action 正式命名而来。6.1.1 还为此补充了澄清初始状态派发行为的测试#485。3.3 为什么用 nil 表示未初始化ReducerStateType?的可选入参是这套设计的根基action 被 reducer 处理时若当前状态为 nilStore 刚创建reducer 便返回默认状态此后所有 action 都基于既有状态计算。这种纯函数 可选入参的形态让状态归约逻辑可以完全独立于 Store 存在便于组合与测试。四、Middleware泛型化与可后置变更4.1 4.0.0泛型类型参数4.0.0 中 Middleware 引入了泛型类型参数#226该泛型与 Store 的State类型一致用于getState方法从而允许在 middleware 代码中直接访问状态无需类型转换。当前定义见 Middleware.swiftpublic typealias DispatchFunction (Action) - Void public typealias MiddlewareState (escaping DispatchFunction, escaping () - State?) - (escaping DispatchFunction) - DispatchFunction这其实是一个柯里化的函数签名middleware 接收下一个 dispatch和读取状态两个闭包返回一个包装后的 dispatch。Store 在createDispatchFunction()中通过reduce把所有 middleware 反向串联、逐层包裹默认派发逻辑见 Store.swiftprivate func createDispatchFunction() - DispatchFunction! { // Wrap the dispatch function with all middlewares return middleware .reversed() .reduce( { [unowned self] action in self._defaultDispatch(action: action) }, { dispatchFunction, middleware in let dispatch: (Action) - Void { [weak self] in self?.dispatch($0) } let getState: () - State? { [weak self] in self?.state } return middleware(dispatch, getState)(dispatchFunction) }) }4.2 6.0.0middleware 后置变更6.0.0允许在 Store 创建之后修改 middleware 列表#427。这在 Store.swift 中实现为middleware属性带didSet一旦赋值就会重新构建dispatchFunctionpublic var middleware: [MiddlewareState] { didSet { dispatchFunction createDispatchFunction() } }这意味着你可以在运行时动态增删中间件例如调试时接入日志或时间旅行工具Store 会立刻用新列表重建派发链。4.3 历史修复内存管理早期版本围绕 middleware 有两项内存管理修复0.2.4修复 middleware 使用时的 retain cyclesendyhalim0.2.5再次修复#66Ben-G。在当前源码中createDispatchFunction里dispatch与getState闭包都使用[weak self]捕获 Store正是这两次修复的沉淀。五、dispatch 语义的演进5.1 返回值从 Any 变为 Void4.0.0将dispatch的返回值从Any改为Void#187。CHANGELOG 明确说明核心团队没有发现返回值Any的实际用例因此直接移除而不提供替代。Redux 生态中常见返回 Promise的用法ReSwift 的态度是不鼓励用返回值打破单向数据流但提供了带callback的dispatch重载store.dispatch(asyncActionCreator) { state in // 异步 action 处理完成、状态更新后的回调 }当前源码中同步dispatch(_ action: Action)即dispatchFunction(action)见 Store.swift本身不返回任何值。5.2 ActionCreator / AsyncActionCreator 的弃用4.1.0弃用了ActionCreator和AsyncActionCreator#391推荐迁移到 ReSwift-Thunk 库现通过独立 Thunk 机制处理副作用。在 Store.swift 中仍保留这些 API但都标注了available(*, deprecated, message: Deprecated in favor of https://github.com/ReSwift/ReSwift-Thunk)。如果你在旧代码中使用它们编译时会看到明确的迁移提示。此外1.0.0曾移除同步 dispatch 方法上的回调参数Ben-G这是 dispatch 语义收敛的第一步。5.3 并发安全isDispatching 原子化6.0.0将isDispatching改为原子操作#341、#446。当前实现位于 Store.swift其类型是 Synchronized.swift 中基于 DispatchQueue 并发队列实现的线程安全包装器private var isDispatching SynchronizedBool(false)_defaultDispatchStore.swift在归约期间锁定该标志若在 reducer 内再次派发 action 或从多线程并发派发会触发raiseFatalError提示 ReSwift:ConcurrentMutationError该错误消息的措辞在 4.0.0 中由 langford 澄清。Synchronized使用读走并发队列、写走 barrier 的方式保证原子性见 Synchronized.swift。六、StandardAction 的离去与 state 的收权6.1 4.1.0弃用4.1.0弃用了StandardAction与StandardActionConvertible官方给出的理由是它们对新手有一定迷惑性且核心库中并无直接用例。6.2 5.0.0彻底移除5.0.0正式移除这两个协议#270并给出明确的迁移指引StandardAction/StandardActionConvertible被移入 ReSwift-Recorder 库——它们本就服务于录制/回放场景若你的项目不依赖 ReSwift-Recorder 却使用了StandardAction可以将旧实现直接复制进自己的项目作为过渡方案。这次移除也体现了 ReSwift 的库边界哲学核心库只保留最小通用能力场景化能力交给生态库。6.3 state 变为 private(set)5.0.0同时将 Store 的state改为private(set)#354这是 ReSwift 在状态只能经由 Action → Reducer 变更这一单向数据流原则上的彻底落实。当前源码 Store.swift 中private(set) public var state: State! { didSet { subscriptions.forEach { if $0.subscriber nil { subscriptions.remove($0) } else { $0.newValues(oldState: oldValue, newState: state) } } } }private(set)意味着外部只能读取、不能写入状态任何绕过 reducer 和 middleware 直接改状态的路径都被封死。同时didSet是订阅通知的唯一入口——状态一旦更新所有订阅者都会收到回调这就把状态变更与通知扩散牢牢绑定在一起。CHANGELOG 也预告了 ReSwift-Recorder 未来需要适配这一变化。七、Store 与订阅的健壮性修复7.1 弱引用订阅者0.2.5起 Store 对订阅者持有弱引用#62vfn避免订阅者生命周期异常导致的意外内存行为。在 Store.swift 与 Subscription.swift 中可以看到subscriptions是SubscriptionBox集合而 box 里的subscriber是weak var。didSet中还会顺手清理已释放的订阅者if $0.subscriber nil { subscriptions.remove($0) }7.2 4.0.1 的三项修复修复SubscriptionBox中的 retain cycle#278修复使用skipRepeats且子状态为可选类型时子状态变为 nil 不通知的 bug为 Equatable 子状态选择增加自动 skipRepeats#300JoeCherry——这是 4.1.0 中automaticallySkipsRepeats功能的直接前身。7.3 6.1.1 的访问级别修正6.1.1将 Store 扩展中的open func改为public func#491理由很直接扩展方法本来就无法被重写overrideopen修饰是误导。这是纯 API 层面的严谨性修正不改变行为。八、平台与 Swift 语言版本的支持边界ReSwift 各版本对语言与平台的取舍同样是 CHANGELOG 的重要主题对选择 ReSwift 版本的用户有直接影响版本变更含义2.1.0Swift 3 预览兼容保持 Swift 2 命名过渡期双兼容3.0.0放弃 Swift 2.2 及以下更新至 Swift 3.0.1全面拥抱 Swift 34.0.1更新至 Swift 4.2跟进 Swift 45.0.0解决 Xcode 10.2 Swift 4.2.2/5.0 警告更新 Swift Package Manager 支持为 Swift 5 铺路6.0.0放弃 Swift 3.2 / 4.0 / 4.1#418放弃 iOS 8#447收窄支持面降低维护成本6.2.0移除旧部署目标要求 macOS 10.13、iOS 12、tvOS 12、watchOS 4不再支持 Swift 5.5 之前的版本#509全面拥抱现代工具链工程层面还有两条值得注意的路线2.1.0将测试框架从 Quick/Nimble 迁移到 XCTest#127并启用 Travis CI 自动构建文档#1284.1.0启用了 Allow app extension API only 构建设置#328使 ReSwift 可以被应用扩展App Extension使用。此外4.0.0引入了DispatchingStoreType协议#197将只派发、不管状态的能力抽象出来便于测试替身见 DispatchingStoreType.swift。九、给迁移者的实用建议综合整个 CHANGELOG不同版本的用户可按以下路径规划升级从 3.x 升级到 4.x最大的工作量在于订阅代码改造——把store.subscribe(subscriber) { ($0.a, $0.b) }改写为store.subscribe(subscriber) { $0.select { ($0.a, $0.b) } }同时把自定义 reducer 类型改写为纯函数dispatch返回值不再依赖。从 4.x 升级到 5.x移除对StandardAction的依赖自行复制旧实现或改用 ReSwift-Recorder接受state变为private(set)的事实所有状态变更回归 Action → Reducer 正轨。从 5.x 升级到 6.x确认工具链满足最低要求Swift 5.5iOS 12 等StateType协议已弃用6.1.0见 State.swift 中的available(*, deprecated)标注可直接删除该协议一致性声明如需要可享受 middleware 后置变更与原子化isDispatching带来的并发安全。弃用 API 的处理ActionCreator/AsyncActionCreator请迁移至 ReSwift-ThunkStandardAction请迁移至 ReSwift-Recorder这两条迁移路径在 CHANGELOG 中均有明确说明。十、结语从 0.2.4 到 6.2.0ReSwift 的演进历程浓缩了 Swift 生态七年的变化语言从 2.x 走到 5.5测试工具从 Quick/Nimble 回归 XCTest包管理从 CocoaPods 扩展至 Swift Package Manager。而在 API 层面ReSwift 始终坚持最小核心 生态扩展的理念——StandardAction交给 Recorder、ActionCreator交给 Thunk、状态写入权收回 Store。如今的 ReSwift订阅用selectskipRepeats组合即可精确控制通知粒度reducer 是纯函数middleware 可按需调整状态变更路径清晰唯一这正是单向数据流架构在 Swift 世界中的成熟形态。赞分享【免费下载链接】ReSwiftUnidirectional Data Flow in Swift - Inspired by Redux项目地址https://gitcode.com/gh_mirrors/re/ReSwift点击查看免费下载相关推荐Moya 版本演进全解析从 0.1 到 15.0 的 API 变迁与技术路线图Moya 版本演进全解析从 0.1 到 15.0 的 API 变迁与技术路线图 导读 本篇文章以仓库中的 Changelog.md https://link.开发工具FlatBuffers Dart 包版本演进全解析从 1.9.0 到 23.5.26 的 API 变迁与实现原理FlatBuffers Dart 包版本演进全解析从 1.9.0 到 23.5.26 的 API 变迁与实现原理 导读 本文以 dart/CHANGELOG.序列化跨平台编译器Tweepy 版本演进全解析从 v1.0 到 v4.x 的 API 变迁与技术路线图Tweepy 版本演进全解析从 v1.0 到 v4.x 的 API 变迁与技术路线图 本文以 tweepy 官方 变更日志 https://link.gitc后端上一篇Grafana实时监控使用Telegraf实现指标流式传输下一篇【保姆级超详细还免费】Django测试工具详解从Test Client到请求模拟创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考