Observer — 订阅变化模式的关键概念

作者: IT Sectr 发布日期: 2026-02-17 阅读时间: 7 分钟

Observer — 一种行为模式,其中一个对象(发布者)通知多个订阅者其状态的变化。在移动开发中,Observer 是响应式机制的基础:UI 订阅数据变化并自动更新。该模式在 iOS 的 NotificationCenter 和 Android 的 LiveData/Flow 中实现。更多信息 — 参见 Refactoring Guru: Observer

要点

  • Observer — 订阅模式:一个发布者,多个订阅者
  • NotificationCenter — iOS/macOS 中 Observer 的内置实现
  • Flow 和 LiveData — Android 中 Observer 的响应式实现
  • Push vs Pull — 发布者可以发送数据或通知事件
  • 内存泄漏 — 订阅者必须取消订阅以防止泄漏

什么是 Observer:观察者模式的本质?

Observer(观察者)— 一种 GoF 行为模式,定义了对象之间的「一对多」依赖关系。当一个对象(Subject 或 Observable)改变状态时,所有依赖对象(Observers)都会自动收到通知并更新。该模式实现了松耦合:发布者不知道订阅者的具体类 — 只知道它们实现了 Observer 接口。

Observer 结构 包括 Subject 接口(具有 attach()、detach()、notify() 方法)和 Observer 接口(具有 update() 方法)。ConcreteSubject 存储状态和订阅者列表。ConcreteObserver 实现 update() 并对变化做出反应。在移动开发中,经典的 GoF 实现很少见 — 它被内置机制取代:NotificationCenter、Combine、Flow、LiveData,它们用现代 API 实现了相同的理念。

Push vs Pull 模型 — 在 Push 模型中,Subject 向所有订阅者发送数据(NotificationCenter.post)。在 Pull 模型中,Subject 仅通知,订阅者自行获取数据。Android LiveData 使用 Push(数据在 observe() 中传递),RxJava/Flow 支持两种模型。选择取决于任务:Push 更适合 UI 更新,Pull 对于订阅者可能不希望接收的大数据量更高效。

iOS 中的 Observer:NotificationCenter、Combine 和 KVO

NotificationCenter — iOS/macOS 中用于实现 Observer 的内置机制。发布者通过 NotificationCenter.default.post(name:, object:, userInfo:) 发送 Notification。订阅者通过 addObserver(forName:, queue:, using:) 注册。NotificationCenter 支持命名通知(Notification.Name)并可以在 userInfo 中传递任何数据。UIKeyboardWillShowNotification、UIApplicationDidEnterBackgroundNotification — 系统示例。

swift
extension Notification.Name {
    static let userDidLogin = Notification.Name("userDidLogin")
}

// 发布者
NotificationCenter.default.post(
    name: .userDidLogin,
    object: nil,
    userInfo: ["userId": "123"]
)

// 订阅者
class ProfileViewModel {
    private var observers: [NSObjectProtocol] = []

    func startObserving() {
        let observer = NotificationCenter.default.addObserver(
            forName: .userDidLogin,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard let userId = notification.userInfo?["userId"] as? String else { return }
            // 订阅者对事件做出反应
            self?.loadProfile(userId: userId)
        }
        observers.append(observer)
    }

    func stopObserving() {
        observers.forEach { NotificationCenter.default.removeObserver($0) }
        observers.removeAll()
    }
}

Combine 框架 — NotificationCenter 的现代响应式替代方案,自 iOS 13 起可用。Publisher(NotificationCenter、URLSession.Timer)— 发布者,Subscriber(sink、assign)— 订阅者。Combine 添加了操作符(map、filter、combineLatest)用于转换数据流。@Published — 自动通知订阅者变化的属性包装器。在使用 SwiftUI 的 MVVM 中,Combine 取代了 NotificationCenter 来连接 ViewModel 和 View。

KVO(Key-Value Observing) — 用于观察对象单个属性的旧 ObjC/Swift 机制。@objc dynamic var name: String — 被观察的属性。observe(.name) — 订阅。KVO 仅适用于与 @objc 兼容的类和 ObjC 继承。Apple 建议在新项目中使用 Combine 和 @Published 代替 KVO。KVO 在混合项目中仍与 UIKit 兼容。

Android 中的 Observer:LiveData、StateFlow 和 SharedFlow

LiveData — Android Architecture Components 中用于实现 Observer 的组件。一个 Observable 类,通知订阅者数据变化。LiveData 考虑生命周期:订阅者(LifecycleOwner)在销毁时自动取消订阅。LiveData 是 Push 模型:数据在 observe() 中传递。LiveData 是 Jetpack Compose 引入之前 Android 中 MVVM 的基本构建块。

kotlin
// ViewModel — 发布者
class UserViewModel : ViewModel() {
    private val _user = MutableLiveData<User?>(null)
    val user: LiveData<User?> = _user

    fun loadUser(id: String) {
        viewModelScope.launch {
            val result = userRepository.getUser(id)
            _user.value = result
        }
    }
}

// Fragment — 订阅者
class UserFragment : Fragment() {
    private val viewModel: UserViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.user.observe(viewLifecycleOwner) { user ->
            // 订阅者对变化做出反应
            userName.text = user?.name
            userEmail.text = user?.email
        }
    }
}

StateFlow 和 SharedFlow — 来自 Kotlin Coroutines 的响应式类型,在 Jetpack Compose 中取代了 LiveData。StateFlow — 具有固定当前值的可观察状态持有者。SharedFlow — 可配置的无状态热流,适用于一次性事件(导航、toast)。两种类型都与 Compose 紧密集成:collectAsState()、collectAsEffect()。在带有 Compose 的现代 Android 项目中,StateFlow 是必需的。

LiveData vs StateFlow — LiveData 绑定到 Android Lifecycle,StateFlow 是平台无关的。StateFlow 支持协程、操作符(map、filter)并在没有 Android 依赖的情况下进行测试。LiveData 对于 Java 兼容性更简单。Google 建议将 StateFlow 用于 Kotlin + Compose 的新项目,将 LiveData 用于支持旧项目或 Java 代码。

订阅管理和内存泄漏

内存泄漏(memory leaks) — 没有正确管理订阅的 Observer 的主要问题。如果订阅者(Activity、Fragment、UIViewController)被销毁但未取消订阅,发布者继续持有对它的引用,垃圾收集器无法释放内存。在 Android 中,LifecycleOwner(Activity/Fragment)必须调用 removeObserver() 或使用 observe(viewLifecycleOwner)。在 iOS 中 — 在 deinit 中调用 removeObserver 或在 Combine 中使用 disposeBag。

平台Observer 机制自动取消订阅手动取消订阅
iOSNotificationCenter在 deinit 中调用 removeObserver()
iOSCombine (sink)store(in: &bag) — DisposeBag
iOSKVO在 deinit 中调用 removeObserver()
AndroidLiveData是 (LifecycleOwner)removeObserver() 可选
AndroidStateFlow通过 viewModelScope取消订阅时调用 cancel() Job
AndroidRxJava在 CompositeDisposable 中调用 dispose()

订阅者中的弱引用(weak reference) — 在订阅块中,在 Swift 中使用 [weak self],在 Kotlin 中引用 lifecycle scope。LiveData 通过 LifecycleOwner 自动管理订阅 — 订阅仅在 Lifecycle 处于 STARTED 或 RESUMED 状态时激活。Compose 中的 StateFlow 使用 collectAsState() 并考虑生命周期。iOS 中的 NotificationCenter 需要显式的 [weak self],因为闭包强引用 self。

Observer vs Publisher-Subscriber:区别是什么

Observer(GoF)和 Publisher-Subscriber(PubSub) — 相似但不同的模式。在 Observer 中,发布者直接通过调用方法通知订阅者。发布者知道订阅者(存储列表)。在 PubSub 中,发布者和订阅者互不知晓 — 它们之间有一个中介(Event Bus、Message Queue、NotificationCenter)。发布者向通道发送消息,订阅者监听通道。PubSub 提供更松散的耦合。

移动开发中的 PubSub 示例 — iOS 中的 NotificationCenter 可视为 PubSub:发布者不知道订阅者 — 它只是发布通知。Android 中的 EventBus 或 Otto(已过时)。带有 BroadcastChannel 的 SharedFlow — Kotlin 世界中的 PubSub。在分布式系统中,PubSub 通过 RabbitMQ、Kafka、Google PubSub 实现。对于移动开发,PubSub 在模块化架构中很有用,其中模块不应相互依赖。

如何选择 — 对于 UI 更新(ViewModel → View),使用 Observer(LiveData、StateFlow、@Published)。对于模块间事件(授权、退出登录、主题更改)— 使用 PubSub(SharedFlow、NotificationCenter、EventBus)。Observer 在单个屏幕内更简单高效,PubSub 对于全局事件更灵活,但由于隐式依赖关系更难调试。

常见问题

StateFlow 和 LiveData 有什么区别?

StateFlow 是来自 Kotlin Coroutines 的独立于平台的类型,LiveData 绑定到 Android Lifecycle。StateFlow 支持协程和操作符,无需 Android 即可测试。LiveData 通过 LifecycleOwner 自动管理订阅。Google 建议将 StateFlow 用于 Kotlin + Compose 的新项目,将 LiveData 用于 Java 兼容性。

如何避免使用 NotificationCenter 造成的内存泄漏?

在 handler 闭包中使用 [weak self] 并在 deinit 中调用 removeObserver()。保存对 observer(NSObjectProtocol)的引用并在对象销毁时删除它。在 Combine 中,使用 AnyCancellable 和 store(in:) 在 DisposeBag 释放时自动取消订阅。

可以在不使用 Combine 的情况下在 SwiftUI 中使用 Observer 吗?

可以,SwiftUI 支持带有 @Published 和 @StateObject/@ObservedObject 的 ObservableObject — 这是 Observer 的内置实现。@Published 自动通知 View 有关变化的信息。Combine 不是必需的:ObservableObject 使用内置于 SwiftUI 的 objectWillChange Publisher。Combine 添加了用于转换流的操作符。

何时使用 SharedFlow 而不是 StateFlow?

SharedFlow — 用于一次性事件(导航、toast、Snackbar),不需要当前值。StateFlow — 用于 UI 状态(数据列表、加载进度),需要当前快照。SharedFlow 没有 value 属性,不会向新订阅者返回最后一个值。

iOS 中 KVO 和 Combine 的区别是什么?

KVO 是过时的 ObjC 机制,需要 @objc dynamic 并且仅适用于继承自 NSObject 的类。Combine 是现代 Swift 框架,类型安全,具有操作符和与 SwiftUI 的集成。Combine 取代了 KVO 和 NotificationCenter。Apple 建议在新项目中使用 Combine,KVO 仅用于支持旧代码。

总结

  • Observer — 用于通知订阅者变化的行为模式
  • iOS NotificationCenter — 带有命名通知的 PubSub 实现
  • iOS Combine — 带有 Publisher 和 Subscriber 的响应式框架
  • Android LiveData — 来自 Android Architecture Components 的生命周期感知 Observer
  • Android StateFlow — 用于 Compose 和协程的响应式状态持有者
  • 内存管理 — 强制取消订阅以防止泄漏
  • Observer vs PubSub — 直接订阅 vs 用于松耦合的中介

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读