Observer — 一种行为模式,其中一个对象(发布者)通知多个订阅者其状态的变化。在移动开发中,Observer 是响应式机制的基础:UI 订阅数据变化并自动更新。该模式在 iOS 的 NotificationCenter 和 Android 的 LiveData/Flow 中实现。更多信息 — 参见 Refactoring Guru: 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 对于订阅者可能不希望接收的大数据量更高效。
NotificationCenter — iOS/macOS 中用于实现 Observer 的内置机制。发布者通过 NotificationCenter.default.post(name:, object:, userInfo:) 发送 Notification。订阅者通过 addObserver(forName:, queue:, using:) 注册。NotificationCenter 支持命名通知(Notification.Name)并可以在 userInfo 中传递任何数据。UIKeyboardWillShowNotification、UIApplicationDidEnterBackgroundNotification — 系统示例。
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 兼容。
LiveData — Android Architecture Components 中用于实现 Observer 的组件。一个 Observable 类,通知订阅者数据变化。LiveData 考虑生命周期:订阅者(LifecycleOwner)在销毁时自动取消订阅。LiveData 是 Push 模型:数据在 observe() 中传递。LiveData 是 Jetpack Compose 引入之前 Android 中 MVVM 的基本构建块。
// 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 机制 | 自动取消订阅 | 手动取消订阅 |
|---|---|---|---|
| iOS | NotificationCenter | 否 | 在 deinit 中调用 removeObserver() |
| iOS | Combine (sink) | 否 | store(in: &bag) — DisposeBag |
| iOS | KVO | 否 | 在 deinit 中调用 removeObserver() |
| Android | LiveData | 是 (LifecycleOwner) | removeObserver() 可选 |
| Android | StateFlow | 通过 viewModelScope | 取消订阅时调用 cancel() Job |
| Android | RxJava | 否 | 在 CompositeDisposable 中调用 dispose() |
订阅者中的弱引用(weak reference) — 在订阅块中,在 Swift 中使用 [weak self],在 Kotlin 中引用 lifecycle scope。LiveData 通过 LifecycleOwner 自动管理订阅 — 订阅仅在 Lifecycle 处于 STARTED 或 RESUMED 状态时激活。Compose 中的 StateFlow 使用 collectAsState() 并考虑生命周期。iOS 中的 NotificationCenter 需要显式的 [weak self],因为闭包强引用 self。
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 是来自 Kotlin Coroutines 的独立于平台的类型,LiveData 绑定到 Android Lifecycle。StateFlow 支持协程和操作符,无需 Android 即可测试。LiveData 通过 LifecycleOwner 自动管理订阅。Google 建议将 StateFlow 用于 Kotlin + Compose 的新项目,将 LiveData 用于 Java 兼容性。
在 handler 闭包中使用 [weak self] 并在 deinit 中调用 removeObserver()。保存对 observer(NSObjectProtocol)的引用并在对象销毁时删除它。在 Combine 中,使用 AnyCancellable 和 store(in:) 在 DisposeBag 释放时自动取消订阅。
可以,SwiftUI 支持带有 @Published 和 @StateObject/@ObservedObject 的 ObservableObject — 这是 Observer 的内置实现。@Published 自动通知 View 有关变化的信息。Combine 不是必需的:ObservableObject 使用内置于 SwiftUI 的 objectWillChange Publisher。Combine 添加了用于转换流的操作符。
SharedFlow — 用于一次性事件(导航、toast、Snackbar),不需要当前值。StateFlow — 用于 UI 状态(数据列表、加载进度),需要当前快照。SharedFlow 没有 value 属性,不会向新订阅者返回最后一个值。
KVO 是过时的 ObjC 机制,需要 @objc dynamic 并且仅适用于继承自 NSObject 的类。Combine 是现代 Swift 框架,类型安全,具有操作符和与 SwiftUI 的集成。Combine 取代了 KVO 和 NotificationCenter。Apple 建议在新项目中使用 Combine,KVO 仅用于支持旧代码。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。