Swift中Reactive扩展实现TwoWayBindDelegate时BindType重声明错误的解决办法
这个问题我之前在做RxSwift双向绑定封装的时候也踩过坑,确实是Swift泛型扩展处理关联类型时的一个已知问题。下面给你几个经过验证的可行解决思路,你可以根据自己的代码结构选择:
方案1:给Reactive类添加额外泛型参数替代关联类型
这种方法通过给Reactive类增加一个对应BindType的泛型参数,让不同UI组件的绑定类型通过泛型组合来区分,从根源上避免重复声明的问题:
// 保持原协议不变 protocol TwoWayBindDelegate: class { associatedtype BindType func twoWayBind(to observable: Observable<BindType>?, observableChanged: ((BindType) -> ())?) } // 修改Reactive类,增加BindType泛型参数并遵循协议 class Reactive<Base: UIView, BindType>: TwoWayBindDelegate { let base: Base let disposeBag = DisposeBag() init(_ base: Base) { self.base = base } // 默认空实现,子类/扩展可重写 func twoWayBind(to observable: Observable<BindType>?, observableChanged: ((BindType) -> ())?) {} } // UISlider专属扩展,指定BindType为Float extension Reactive where Base == UISlider, BindType == Float { override func twoWayBind(to observable: Observable<Float>?, observableChanged: ((Float) -> ())?) { // UISlider双向绑定逻辑 observable?.bind(to: base.rx.value).disposed(by: disposeBag) base.rx.value.subscribe(onNext: { value in observableChanged?(value) }).disposed(by: disposeBag) } } // UITextField专属扩展,指定BindType为String extension Reactive where Base == UITextField, BindType == String { override func twoWayBind(to observable: Observable<String>?, observableChanged: ((String) -> ())?) { // UITextField双向绑定逻辑 observable?.bind(to: base.rx.text).disposed(by: disposeBag) base.rx.text.orEmpty.subscribe(onNext: { text in observableChanged?(text) }).disposed(by: disposeBag) } }
优点:逻辑直接,完全避开了关联类型的冲突问题;缺点:需要修改Reactive类的泛型声明,可能会影响现有代码的调用方式(比如需要显式指定BindType:let reactiveSlider = Reactive<UISlider, Float>(slider))。
方案2:将协议实现转移到UI组件自身的扩展
把TwoWayBindDelegate的实现从Reactive扩展转移到对应的UI组件扩展中,再通过Reactive类封装调用,这样就不会在Reactive的多个扩展里重复声明关联类型了:
protocol TwoWayBindDelegate: class { associatedtype BindType func twoWayBind(to observable: Observable<BindType>?, observableChanged: ((BindType) -> ())?) } // 保持原Reactive类结构不变 class Reactive<Base: UIView> { let base: Base let disposeBag = DisposeBag() init(_ base: Base) { self.base = base } } // 让UISlider遵循协议并实现绑定逻辑 extension UISlider: TwoWayBindDelegate { typealias BindType = Float func twoWayBind(to observable: Observable<Float>?, observableChanged: ((Float) -> ())?) { guard let observable = observable else { return } observable.bind(to: rx.value).disposed(by: rx.disposeBag) rx.value.subscribe(onNext: observableChanged).disposed(by: rx.disposeBag) } } // Reactive扩展封装UISlider的绑定调用 extension Reactive where Base == UISlider { func twoWayBind(to observable: Observable<Float>?, observableChanged: ((Float) -> ())?) { base.twoWayBind(to: observable, observableChanged: observableChanged) } } // UITextField同理 extension UITextField: TwoWayBindDelegate { typealias BindType = String func twoWayBind(to observable: Observable<String>?, observableChanged: ((String) -> ())?) { guard let observable = observable else { return } observable.bind(to: rx.text).disposed(by: rx.disposeBag) rx.text.orEmpty.subscribe(onNext: observableChanged).disposed(by: rx.disposeBag) } } extension Reactive where Base == UITextField { func twoWayBind(to observable: Observable<String>?, observableChanged: ((String) -> ())?) { base.twoWayBind(to: observable, observableChanged: observableChanged) } }
优点:不需要修改Reactive类的核心结构,符合RxSwift的常规扩展思路;缺点:绑定逻辑分散在UI组件的扩展中,若后续有大量组件需要绑定,代码会比较零散。
方案3:使用类型擦除封装绑定逻辑
如果不想修改原有的Reactive和协议结构,可以通过类型擦除的方式,把不同组件的绑定逻辑封装成独立的类型,再注入到Reactive类中:
protocol TwoWayBindDelegate: class { associatedtype BindType func twoWayBind(to observable: Observable<BindType>?, observableChanged: ((BindType) -> ())?) } // 类型擦除的绑定器 class AnyTwoWayBinder<BindType>: TwoWayBindDelegate { private let _bind: (Observable<BindType>?, ((BindType) -> ())?) -> Void init<B: TwoWayBindDelegate>(_ binder: B) where B.BindType == BindType { _bind = binder.twoWayBind } func twoWayBind(to observable: Observable<BindType>?, observableChanged: ((BindType) -> ())?) { _bind(observable, observableChanged) } } // 保持Reactive类不变,增加绑定器存储属性 class Reactive<Base: UIView> { let base: Base let disposeBag = DisposeBag() private var binder: AnyTwoWayBinder<Any>? init(_ base: Base) { self.base = base // 根据Base类型初始化对应的绑定器 if let slider = base as? UISlider { binder = AnyTwoWayBinder(SliderBinder(slider: slider)) } else if let textField = base as? UITextField { binder = AnyTwoWayBinder(TextFieldBinder(textField: textField)) } } func twoWayBind<BindType>(to observable: Observable<BindType>?, observableChanged: ((BindType) -> ())?) { guard let binder = binder as? AnyTwoWayBinder<BindType> else { return } binder.twoWayBind(to: observable, observableChanged: observableChanged) } } // UISlider专属绑定器 class SliderBinder: TwoWayBindDelegate { typealias BindType = Float private let slider: UISlider private let disposeBag = DisposeBag() init(slider: UISlider) { self.slider = slider } func twoWayBind(to observable: Observable<Float>?, observableChanged: ((Float) -> ())?) { observable?.bind(to: slider.rx.value).disposed(by: disposeBag) slider.rx.value.subscribe(onNext: observableChanged).disposed(by: disposeBag) } } // UITextField专属绑定器 class TextFieldBinder: TwoWayBindDelegate { typealias BindType = String private let textField: UITextField private let disposeBag = DisposeBag() init(textField: UITextField) { self.textField = textField } func twoWayBind(to observable: Observable<String>?, observableChanged: ((String) -> ())?) { observable?.bind(to: textField.rx.text).disposed(by: disposeBag) textField.rx.text.orEmpty.subscribe(onNext: observableChanged).disposed(by: disposeBag) } }
优点:完全隔离不同组件的绑定逻辑,扩展性强;缺点:实现相对复杂,需要额外的类型擦除层和绑定器类。
内容的提问来源于stack exchange,提问作者Nader
相关产品推荐
相关产品推荐

