RxSwift 中的 DisposeBag解析

DisposeBagRxSwift 中用于管理资源释放的核心类之一,它的主要作用是 自动清理订阅的资源 ,防止内存泄漏或悬空指针问题。它是 RxSwift 实现 响应式编程中的资源生命周期管理 的关键工具。


🧠 核心功能

  1. 自动释放订阅资源

    DisposeBag 被释放(如视图控制器被销毁)时,它会自动调用所有已注册的 Disposable 对象的 dispose() 方法,释放资源(如网络请求、观察者等)。

  2. 线程安全

    通过内部的 SpinLock(自旋锁)确保多线程环境下对 disposables 数组的安全访问。

  3. 简化资源管理

    开发者只需将 Disposable 添加到 DisposeBag,无需手动调用 dispose(),避免手动管理资源的复杂性。


🧩 源码解析

1. 基本结构

swift 复制代码
public final class DisposeBag: DisposeBase {
    private var lock = SpinLock()
    private var disposables = [Disposable]()
    private var isDisposed = false

    deinit {
        self.dispose() // 析构时自动释放所有资源
    }
}
  • disposables :存储所有待释放的 Disposable 对象。
  • isDisposed:标记是否已释放。
  • deinit :当 DisposeBag 被销毁时,自动调用 dispose() 释放所有资源。

2. 添加资源

swift 复制代码
public func insert(_ disposable: Disposable) {
    self._insert(disposable)?.dispose()
}

private func _insert(_ disposable: Disposable) -> Disposable? {
    self.lock.performLocked {
        if self.isDisposed {
            return disposable // 已释放,则直接调用 dispose()
        }
        self.disposables.append(disposable)
        return nil
    }
}
  • 如果 DisposeBag 尚未释放,将 Disposable 添加到 disposables
  • 如果 DisposeBag 已释放,则直接调用 disposable.dispose()

3. 释放资源

swift 复制代码
private func dispose() {
    let oldDisposables = self._dispose()
    for disposable in oldDisposables {
        disposable.dispose()
    }
}
  • 遍历所有 disposables,逐个调用 dispose() 释放资源。

4. 便捷初始化

swift 复制代码
public convenience init(disposing disposables: [Disposable]) {
    self.init()
    self.disposables += disposables
}
  • 允许一次性添加多个 DisposableDisposeBag

📌 使用场景

典型用法

swift 复制代码
let disposeBag = DisposeBag()

// 网络请求订阅
networkService.getData()
    .subscribe(onNext: { data in
        print("Received data: $data)")
    })
    .disposed(by: disposeBag)

// UI 事件订阅
button.rx.tap
    .subscribe(onNext: { 
        print("Button tapped")
    })
    .disposed(by: disposeBag)
  • disposed(by:) :将 Disposable 添加到 DisposeBag,由其自动管理生命周期。
  • DisposeBag 通常作为 UIViewControllerViewModel 的属性,在其生命周期结束时自动释放所有资源。

⚠️ 注意事项

  1. 避免手动调用 dispose()

    手动调用 dispose() 通常不推荐,因为 DisposeBag 已经通过 deinit 自动清理资源。除非你需要提前释放资源(如复杂逻辑),否则应依赖 DisposeBag

  2. 避免 DisposeBag 滥用

    • 不要创建过多 DisposeBag 实例,通常一个 UIViewControllerViewModel 使用一个 DisposeBag 即可。
    • 如果需要动态管理资源(如切换页面时立即释放),可以使用 CompositeDisposable 替代。
  3. 线程安全
    DisposeBag 内部通过 SpinLock 确保线程安全,开发者无需额外处理并发问题。


🔄 CompositeDisposable 的区别

特性 DisposeBag CompositeDisposable
自动释放 ✅ 在 deinit 时自动释放 ❌ 需手动调用 dispose()
用途 适合生命周期管理(如 UIViewController 适合动态管理资源(如切换页面、模块)
线程安全
是否可重用 ❌ 一旦释放不可复用 ✅ 可多次添加/移除资源

📚 实际开发中的价值

  1. 避免内存泄漏

    例如:网络请求或 UI 事件订阅未及时释放,可能导致视图控制器无法被回收。

  2. 简化代码

    不需要手动管理 Disposable 的释放,代码更简洁。

  3. 生命周期绑定

    DisposeBag 作为 UIViewController 的属性,确保资源在视图控制器销毁时自动释放。


🧪 示例代码

swift 复制代码
class RxSwiftBaseUseController: UIViewController {
    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        // 网络请求
        APIService.shared.fetchData()
            .subscribe(onNext: { data in
                print("Data fetched: $data)")
            })
            .disposed(by: disposeBag)

        // 按钮点击事件
        button.rx.tap
            .subscribe(onNext: { 
                print("Button tapped")
            })
            .disposed(by: disposeBag)
    }
}
  • RxSwiftBaseUseController 被销毁时,disposeBag 会自动释放所有订阅的资源。

🧩 总结

DisposeBag 是 RxSwift 中资源管理的核心工具,通过 自动释放订阅资源,简化了响应式编程中的生命周期管理。它结合 Swift 的 ARC(自动引用计数)机制,确保资源在合适的时机释放,避免内存泄漏和悬空指针问题。开发者应将其作为视图控制器或模块的标准属性,合理使用以提升代码的健壮性和可维护性。

相关推荐
saber_andlibert1 小时前
TCMalloc底层实现
java·前端·网络
逍遥德1 小时前
如何学编程之01.理论篇.如何通过阅读代码来提高自己的编程能力?
前端·后端·程序人生·重构·软件构建·代码规范
冻感糕人~1 小时前
【珍藏必备】ReAct框架实战指南:从零开始构建AI智能体,让大模型学会思考与行动
java·前端·人工智能·react.js·大模型·就业·大模型学习
程序员agions1 小时前
2026年,“配置工程师“终于死绝了
前端·程序人生
alice--小文子1 小时前
cursor-mcp工具使用
java·服务器·前端
晚霞的不甘2 小时前
揭秘 CANN 内存管理:如何让大模型在小设备上“轻装上阵”?
前端·数据库·经验分享·flutter·3d
小迷糊的学习记录2 小时前
0.1 + 0.2 不等于 0.3
前端·javascript·面试
梦帮科技2 小时前
Node.js配置生成器CLI工具开发实战
前端·人工智能·windows·前端框架·node.js·json
VT.馒头3 小时前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript
css趣多多3 小时前
一个UI内置组件el-scrollbar
前端·javascript·vue.js