RxSwift:为何NotificationCenter无需管理观察者的生命周期?

101 阅读6分钟

前言

如果看过RxSwift的文档,一定看过官方这样一个例子。

传统实现方法:

var token: NSObjectProtocol!

override func viewDidLoad() {
    super.viewDidLoad()

    token = NotificationCenter.default.addObserver(
          forName: UIApplication.didBecomeActiveNotification,
          object: nil, queue: nil) { (notification) in
        print("Application Will Enter Foreground")
    }
}

deinit {
    NotificationCenter.default.removeObserver(token)
}

通过 Rx 来实现:


let disposeBag = DisposeBag()

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.rx
        .notification(UIApplication.didBecomeActiveNotification)
        .subscribe(onNext: { (notification) in
            print("Application Will Enter Foreground")
        })
        .disposed(by: disposeBag)
}

为何在Rx框架下,NotificationCenter无需管理观察者的生命周期?

让我们一步一步往下看。

RxCocoa/NotificationCenter+Rx.swift源码解析

好的,下面解析一下 NotificationCenter.default.rx.notification 的内部代码实现原理(以 RxSwift 6.x 为例):

1. 扩展入口

RxSwift 通过对 NotificationCenter 的扩展,增加了 rx 属性:

// RxCocoa/NotificationCenter+Rx.swift
extension Reactive where Base: NotificationCenter {
    public func notification(_ name: Notification.Name, object: AnyObject? = nil) -> Observable<Notification> {
        return Observable.create { observer in
            let token = self.base.addObserver(forName: name, object: object, queue: nil) { notification in
                observer.onNext(notification)
            }
            return Disposables.create {
                self.base.removeObserver(token)
            }
        }
    }
}

2. 实现细节

  • Observable.create 创建一个 Observable。
  • 当有订阅者时,调用 addObserver(forName:object:queue:using:) 注册通知观察者。
  • 每当收到通知时,调用 observer.onNext(notification),把通知事件推送给订阅者。
  • 返回一个 Disposable,当订阅释放时(比如 disposeBag 释放),会自动调用 removeObserver(token) 移除观察者。

3. 生命周期管理

  • 只要订阅还在,观察者就存在。
  • 当订阅释放,Disposable 的清理逻辑会移除观察者,避免内存泄漏。

4. 小结

  • NotificationCenter.default.rx.notification 本质上是用 RxSwift 的 Observable 包装了系统的通知注册和移除逻辑,自动管理观察者的生命周期,简化了通知的使用。
  • Observable.create函数入参是传入一个闭包:@escaping (AnyObserver<Element>) -> Disposable,注意此闭包需要返回Disposable
  • Disposables.create的入参也是传入一个闭包create(with dispose: @escaping () -> Void)

代码整体书写与理解并不好懂,简单来说,就是通过分类进行了观察者的移除,所以我们在调用的时候无需再调用removeObserver方法了。

那么Rx里面的removeObserver又是何时被调用的呢?我们接着继续。

RxSwift/AnonymousDisposable.swift源码解析

这个类有点长,我就只贴部分关键代码进行解析:

extension Disposables {
    public static func create(with dispose: @escaping () -> Void) -> Cancelable {
        AnonymousDisposable(disposeAction: dispose)
    }
}

这段代码就是我们用的非常多的Disposables.create的具体方法,它会去构建一个AnonymousDisposable类,入参就是一个disposeAction(处理动作)。

在NotificationCenter+Rx.swif中,传入的disposeAction就是self.base.removeObserver(token)

然后我们看看AnonymousDisposable中的关键代码:

private final class AnonymousDisposable : DisposeBase, Cancelable {
    public typealias DisposeAction = () -> Void

    private let disposed = AtomicInt(0)
    private var disposeAction: DisposeAction?

    fileprivate init(disposeAction: @escaping DisposeAction) {
        self.disposeAction = disposeAction
        super.init()
    }

    fileprivate func dispose() {
        if fetchOr(self.disposed, 1) == 0 {
            if let action = self.disposeAction {
                self.disposeAction = nil
                action()
            }
        }
    }
}

1. 主要属性

  • disposeAction:存储你传入的释放闭包。

2. 构造方法

fileprivate init(disposeAction: @escaping DisposeAction)
  • 构造时传入释放闭包,保存到 disposeAction

3. dispose 方法

fileprivate func dispose() {
    if fetchOr(self.disposed, 1) == 0 {
        if let action = self.disposeAction {
            self.disposeAction = nil
            action()
        }
    }
}
  • 通过原子操作保证只执行一次释放逻辑。
  • 如果action存在,则执行 disposeAction,并将其置为 nil,防止重复调用或者释放。

4. 小结

  • AnonymousDisposable 用于包装一个自定义的释放动作,保证释放动作只会被执行一次且线程安全。
  • 你通常通过 Disposables.create(with:) 来创建它。
  • 这是 RxSwift 资源释放和订阅管理的基础设施之一。

好了,到这里,我们就顺藤摸瓜找到了传入的self.base.removeObserver(token)被保存到了哪里,并且知道了在AnonymousDisposable中通过调用dispose()方法进行了执行,那么这个dispose()又是何时被执行的呢?

我们离真相越来越近了。

RxSwift/DisposeBag.swift源码解析

我贴出了DisposeBag的关键代码:

public final class DisposeBag: DisposeBase {

    private var disposables = [Disposable]()
    
    public override init() {
        super.init()
    }

    public func insert(_ disposable: Disposable) {
        self._insert(disposable)?.dispose()
    }

    private func dispose() {
        let oldDisposables = self._dispose()

        for disposable in oldDisposables {
            disposable.dispose()
        }
    }
    
    deinit {
        self.dispose()
    }
}
  • DisposeBag对象析构函数被调用的时候,会调用它自己的dispose()方法
  • dispose()方法内部,会将disposables数组的中每个Disposable元素进行遍历,并调用Disposable对象的dispose()方法。

到此,我们可以看出最开始的self.base.removeObserver(token)这个移除观察者的方法会在DisposeBag对象的析构函数中调用。

即可以认为self.base.removeObserver(token)是在DisposeBag的生命周期中的销毁阶段完成了移除!

如果上面的代码觉得看的不够清晰,我们来一段拆分的代码。

一个NotificationCenter.default.rx 的生成序列、被订阅、最后订阅被销毁的完整流程

1. Observable 生成

你可以通过多种方式创建 Observable,例如:

let observable = NotificationCenter.default.rx.notification(UIApplication.didBecomeActiveNotification)

2. 被订阅

let disposable = observable.subscribe(onNext: { notification in
                    print("App became active")
                })

流程:

  • subscribe 会触发 Observable 的事件推送逻辑。
  • 你传入的闭包会被包装成一个观察者(Observer)。
  • Observable 会把事件(如 onNext、onCompleted)推送给 Observer。
  • subscribe 返回一个 Disposable,用于后续取消订阅。

3. 订阅被销毁

使用 DisposeBag

let disposeBag = DisposeBag()

let disposable = observable.subscribe(onNext: { value in
    print(value)
})

disposable.disposed(by: disposeBag)
  • disposeBag 被释放时(比如控制器销毁),它会自动调用所有持有的 Disposabledispose() 方法,自动取消订阅。
  • .disposed(by: disposeBag)这个方法,只是一个对disposeBag.insert(disposable)简单封装,方便链式调用
extension Disposable {
    public func disposed(by bag: DisposeBag) {
        bag.insert(self)
    }
}

4. 小结

来,让我们将上面的这个周期的代码在一个ViewController中进行一个完整演示

class ViewController: UIViewController {
    /// 创建一个与ViewController对象生命周期绑定的disposeBag,ViewController销毁,disposeBag就销毁
    let disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        /// 生成序列
        let observable = NotificationCenter.default.rx.notification(UIApplication.didBecomeActiveNotification)

        /// 订阅序列,生成一个可以处理的disposable
        let disposable = observable.subscribe(onNext: { notification in
                    print("App became active")
                })
        /// 将disposable丢入disposeBag中,当ViewController销毁对象销毁时,disposable就会回被销毁,disposable里面封装的removeObserver(token)也会被销毁
        disposable.disposed(by: disposeBag)
    }
}

总结

本文通过对比传统的 NotificationCenter 观察者管理方式与 RxSwift 的响应式实现,深入剖析了 RxSwift 如何自动管理观察者的生命周期,避免了手动移除观察者的繁琐和潜在的内存泄漏风险。

在 RxSwift 中,NotificationCenter.default.rx.notification 通过 Observable 的机制,将系统通知的注册与移除过程封装起来。当你订阅通知时,RxSwift 自动注册观察者,并返回一个 Disposable;当你将 Disposable 加入到 DisposeBag 后,随着 DisposeBag 的销毁(通常与控制器生命周期绑定),RxSwift 会自动调用 Disposable 的 dispose 方法,最终移除观察者。这一切都通过源码中的闭包传递和资源管理类(如 AnonymousDisposable、DisposeBag)实现,保证了释放动作只会被执行一次且线程安全。

通过源码分析和流程梳理,我们可以清晰地看到 RxSwift 在资源管理上的优雅设计:

  • 事件订阅与资源释放解耦,开发者无需关心何时移除观察者;
  • 只需将 Disposable 交给 DisposeBag,生命周期管理自动完成;
  • 极大提升了代码的安全性和可维护性。

总之,RxSwift 的响应式封装让通知监听变得更简单、更安全、更易维护,是现代 iOS 开发中推荐的最佳实践之一。

你也可以在Swift的原生Combine框架中找到类似的处理方式,这里就不过多赘述。