RxSwift自定义视图PublishRelay已接收值但订阅无响应问题咨询
问题可能原因
- 视图控制器侧的订阅使用了局部DisposeBag:如果订阅时绑定的DisposeBag是方法内的局部变量,方法执行完成后DisposeBag会被回收,对应的订阅会立即被自动取消,自然收不到后续事件。
- KeyboardView实例被提前释放:如果你的KeyboardView没有被声明为视图控制器的全局存储属性,而是作为局部变量创建添加到视图后没有强引用持有,实例会被ARC提前回收,Relay也会随之销毁,无法触发订阅回调。
- 订阅时机晚于事件发送:
PublishRelay是热可观察序列,只会将订阅之后产生的事件发送给观察者,如果你是在按钮点击触发后才执行订阅逻辑,之前产生的事件不会被响应。 - 调度规则不匹配:虽然内部按钮事件已经加了
observeOn(MainScheduler.instance),但标准ControlEvent要求事件必须在主线程发送,当前的Reactive扩展没有显式保证外层ControlEvent的事件在主线程分发,极端情况下会出现订阅回调不触发的问题。
修复方案
- 确保视图控制器侧的订阅绑定到控制器的全局DisposeBag属性,同时全局持有KeyboardView实例:
class ViewController: UIViewController { // 声明为全局存储属性,避免被提前回收 private let disposeBag = DisposeBag() private let keyboardView = KeyboardView() override func viewDidLoad() { super.viewDidLoad() // 订阅逻辑在视图加载阶段执行,早于用户点击操作 keyboardView.rx.buttonTap .subscribe(onNext: { action in print("收到按键事件: \(action)") }) .disposed(by: disposeBag) } }
- 完善Reactive扩展的ControlEvent实现,显式保证主线程调度,符合ControlEvent的官方规范:
extension Reactive where Base: KeyboardView { internal var buttonTap: ControlEvent<ActionType> { let source = base.buttonTappedRelay .asObservable() .observe(on: MainScheduler.instance) return ControlEvent(events: source) } }
- 可选优化:如果需要接收订阅前已经触发的事件,可以把
PublishRelay替换为BehaviorRelay,设置默认值即可接收订阅前的最后一次事件。
内容的提问来源于stack exchange,提问作者Andrey Isaev
相关产品推荐
相关产品推荐

