You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RxSwift绑定disposeBag后视图关闭 订阅未正常销毁问题排查

问题根因

所有绑定了disposeBag的订阅无法自动销毁,核心原因只有一个:持有disposeBag的控制器实例,在模态页关闭、页面退出时根本没有被系统回收释放。DisposeBag的销毁逻辑完全依赖自身的deinit方法触发,只要它所属的对象不释放,bag里存的所有订阅就永远不会执行dispose。

具体排查方向
  • 首先排除继承结构的问题:父类定义disposeBag、子类调用.disposed(by:)是完全合规的写法,只要父类的bag是正常的实例存储属性,继承关系本身不会导致释放异常。
  • 你贴的0.5秒延迟异步代码不是核心诱因:虽然guard let self = self会在闭包执行的瞬间强持有控制器,但闭包执行完就会释放引用,最多导致控制器延迟0.5秒释放,不会出现永久不销毁的问题。
  • 优先排查控制器内存泄漏:这是90%以上同类问题的根源。常见泄漏点包括:
    • 代理属性没有用weak修饰,比如你实现的PinCodeViewControllerDelegate,如果对应的delegate属性是strong修饰,就会和控制器形成循环引用
    • 持有的闭包属性没有用[weak self]捕获弱引用,包括自定义的回调闭包、第三方库的completion闭包
    • 定时器、CADisplayLink、动画、全局通知没有在页面退出时手动移除,持续强持有控制器
    • TabBar的子控制器持有逻辑异常,比如每次切换Tab都新建控制器实例但旧实例没有被移除,持续被其他对象强引用
      验证方法很简单:在TechnicianDashboardViewController和父类BaseViewController里都加上deinit打日志,关闭模态页之后如果看不到deinit的打印,就可以确定是控制器泄漏,和Rx本身的写法无关。
deinit {
    print("TechnicianDashboardViewController 已释放")
}
  • 其次检查disposeBag的定义是否正确:如果控制器能正常触发deinit但订阅依然不销毁,就去看父类里disposeBag的定义,必须是存储属性,不能是每次访问都返回新实例的计算属性,也不能被子类意外重写。正确的定义参考:
class BaseViewController: UIViewController {
    let disposeBag = DisposeBag()
}
  • 最后检查Rx绑定本身的引用问题:你贴的几段绑定代码里[weak self]的写法是正确的,但要注意tableView.rx.items的绑定闭包里,不要直接强引用cell或者其他子视图做长期持有,避免形成视图层的循环引用。

关于临时方案的说明

在viewWillDisappear里手动清空disposeBag、或者逐个取消订阅,本质是强制触发订阅的销毁逻辑,绕过了控制器不释放的问题,但内存泄漏的问题依然存在,App长期运行会出现内存持续上涨、回调重复触发的问题,不能作为最终解决方案。

内容的提问来源于stack exchange,提问作者Joakim Sjöstedt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 03:16:07