Swift泛型问题:如何存储任意类型Store数组并比较状态
嘿,这个问题我之前做状态管理时也碰到过,核心就是泛型协议的关联类型限制导致没法直接存数组,还有Equatable约束的冲突问题。咱们一步步来解决:
核心问题拆解
你遇到的两个关键坑:
Store<Any>行不通,因为Any不遵循你的State协议(而State要求实现Equatable);- 带关联类型的
Store协议不能直接作为数组元素,Swift不允许把带Self或关联类型要求的协议当作具体类型使用。
不过别担心,有两种靠谱的解决方案,分别对应你“保留Equatable”和“不依赖Equatable”的需求:
方案1:用类型擦除(Type Erasure)统一存储任意Store
类型擦除是Swift处理带关联类型协议的标准操作,本质是用一个具体类包装所有符合协议的泛型实例,隐藏掉具体的关联类型。
步骤1:修正协议命名(避免冲突)
先把你的Store协议改个名字,避免和类名冲突,同时调整关联类型命名:
import RxSwift protocol State: Equatable {} // 重命名协议为StoreProtocol,避免和类名冲突 protocol StoreProtocol: class { associatedtype StateType: State var processor: PublishSubject<StateType> { get } var _state: StateType { get set } var state: StateType { get set } func initialState() -> StateType func flowable() -> Observable<StateType> func initialize() } // 保留原来的协议扩展逻辑 extension StoreProtocol { var state: StateType { get { return _state } set(value) { if value != state { _state = value processor.onNext(value) } } } func flowable() -> Observable<StateType> { return processor.startWith(state) } func initialState() -> StateType { return StateType.init() } } // 让原来的Store类遵循StoreProtocol class Store<S: State>: StoreProtocol { typealias StateType = S private(set) var processor = PublishSubject<S>() var _initialState: S var _state: S init(initialState: S) { _initialState = initialState _state = initialState } func initialState() -> S { return _initialState } func initialize() { preconditionFailure("This method must be overridden") } }
步骤2:实现类型擦除的AnyStore
创建AnyStore类,包装任意符合StoreProtocol的实例,对外暴露统一接口:
class AnyStore { private let _getState: () -> Any private let _setState: (Any) -> Void private let _getFlowable: () -> Observable<Any> private let _initialize: () -> Void private let _isStateEqual: (Any) -> Bool init<ConcreteStore: StoreProtocol>(_ store: ConcreteStore) { // 封装具体Store的操作 _getState = { store.state } _setState = { newValue in guard let typedValue = newValue as? ConcreteStore.StateType else { return } store.state = typedValue } _getFlowable = { store.flowable().map { $0 as Any } } _initialize = { store.initialize() } // 封装Equatable比较逻辑 _isStateEqual = { other in guard let otherTyped = other as? ConcreteStore.StateType else { return false } return store.state == otherTyped } } // 对外暴露统一的状态访问接口 var state: Any { get { _getState() } set { _setState(newValue) } } // 对外暴露状态流 func flowable() -> Observable<Any> { _getFlowable() } // 初始化方法 func initialize() { _initialize() } // 状态比较方法 func isStateEqual(to other: Any) -> Bool { _isStateEqual(other) } }
步骤3:在Logger里使用AnyStore数组
现在你的Logger可以轻松存储任意类型的Store了:
class Logger { private var stores: [AnyStore] = [] private let disposeBag = DisposeBag() func addStore(_ store: AnyStore) { stores.append(store) } // 示例:跟踪所有状态变化 func trackAllStateChanges() { stores.forEach { store in store.flowable().subscribe(onNext: { newState in print("State changed to: \(newState)") }).disposed(by: disposeBag) } } } // 使用示例 struct AppState: State { var count: Int } let logger = Logger() let appStore = Store(initialState: AppState(count: 0)) logger.addStore(AnyStore(appStore)) logger.trackAllStateChanges()
方案2:不依赖Equatable的状态比较
如果你的某些State类型很难遵循Equatable(比如包含闭包、资源引用等),可以用自定义比较逻辑替代Equatable:
步骤1:修改State协议,添加自定义比较方法
protocol State { // 自定义比较方法,替代Equatable的== func isEqual(to other: Self) -> Bool } // 让遵循Equatable的State自动实现isEqual方法 extension State where Self: Equatable { func isEqual(to other: Self) -> Bool { return self == other } }
步骤2:修改Store类,支持自定义比较逻辑
class Store<S: State> { private var _initialState: S private var _state: S private var processor = PublishSubject<S>() // 允许注入自定义比较闭包,默认用State的isEqual方法 private let stateComparator: (S, S) -> Bool init(initialState: S, stateComparator: @escaping (S, S) -> Bool = { $0.isEqual(to: $1) }) { _initialState = initialState _state = initialState self.stateComparator = stateComparator } var state: S { get { return _state } set(value) { // 用自定义比较替代== if !stateComparator(value, state) { _state = value processor.onNext(value) } } } // ... 其他方法不变 }
步骤3:结合类型擦除统一存储
和方案1一样,你可以创建AnyStore来包装这种自定义比较的Store,存入Logger的数组中。这种方式灵活性更高,适合复杂状态的场景。
总结
- 如果你的
State都能遵循Equatable,方案1的类型擦除是最直接的解决方式,既保留类型安全,又能统一存储; - 如果有无法遵循
Equatable的State,方案2的自定义比较逻辑可以完美替代,同时也能结合类型擦除实现统一存储。
内容的提问来源于stack exchange,提问作者Francisco Durdin Garcia
相关产品推荐
相关产品推荐

