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
相关产品推荐
相关产品推荐

