书接上回
上篇文章我们从RxSwift体系下NotificationCenter无需管理观察者的生命周期出发,剖析了其回收机制的原理。回看原文请戳RxSwift:为何NotificationCenter无需管理观察者的生命周期?
RxSwift核心优势在于:
- RxSwift 会在你订阅通知时自动注册观察者,并在订阅释放(如 DisposeBag 销毁)时自动移除观察者。
- 你无需再手动管理观察者的生命周期,极大降低了出错概率和维护成本。
以上特性是本次封装的基础。下面我开始"秀"代码。
Talk is cheap, Show me the code
这个 Eventable.swift 文件的核心作用是对 iOS 通知(Notification)机制进行协议化和 RxSwift 化的封装,让通知的发送和监听更加类型安全、简洁、易用,尤其适合响应式编程场景。
swift
import Foundation
import RxSwift
import RxCocoa
protocol Eventable: RawRepresentable where RawValue == String {
func post(object: AnyObject?, userInfo: [AnyHashable: Any]?)
func rx(object: AnyObject?) -> Observable<Notification>
var rx: Observable<Notification> { get }
}
extension Eventable {
func post(object: AnyObject? = nil, userInfo: [AnyHashable: Any]? = nil) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: rawValue), object: object, userInfo: userInfo)
}
func rx(object: AnyObject? = nil) -> Observable<Notification> {
return NotificationCenter.default.rx.notification(Notification.Name(rawValue: rawValue), object: object)
}
var rx: Observable<Notification> {
return NotificationCenter.default.rx.notification(Notification.Name(rawValue: rawValue))
}
}
1. 结构与实现思路
1.1 协议定义
swift
protocol Eventable: RawRepresentable where RawValue == String {
func post(object: AnyObject?, userInfo: [AnyHashable: Any]?)
func rx(object: AnyObject?) -> Observable<Notification>
var rx: Observable<Notification> { get }
}
- Eventable 协议要求实现者是
RawRepresentable
且RawValue
为String
,这意味着它通常用于enum
。 - 提供了发送通知 (
post
)和响应式监听通知 (rx
)的方法。
1.2 默认实现
swift
extension Eventable {
func post(object: AnyObject? = nil, userInfo: [AnyHashable: Any]? = nil) { ... }
func rx(object: AnyObject? = nil) -> Observable<Notification> { ... }
var rx: Observable<Notification> { ... }
}
- 通过扩展提供了默认实现,简化了使用。
post
方法用于发送通知。rx
方法和属性用于通过 RxSwift 监听通知,返回Observable<Notification>
,方便响应式处理。
2. 设计思路
- 类型安全:用枚举代替字符串,避免通知名拼写错误。
- 协议扩展:通过协议和扩展,减少重复代码,统一通知的发送和监听方式。
- 响应式支持:集成 RxSwift,方便在响应式编程场景下监听通知。
- 易用性:调用简单,后面示例用法会给出具体例子。
3. 应用场景
- 模块间解耦通信
不同模块、控制器、View 之间通过通知传递事件,避免直接引用,降低耦合。 - 响应式通知监听
在 RxSwift 项目中,直接用.rx
监听通知,配合subscribe
、bind
等操作符,代码更简洁。 - 避免硬编码
所有通知名集中管理,便于维护和查找,减少出错概率。 - 便于测试和扩展
统一的事件管理方式,便于单元测试和后续扩展。
4. 示例用法
EventBus.userLogin.post()
或 EventBus.userLogin.rx.subscribe(...)
。
dart
enum EventBus: String {
case userLogin
case userLogout
}
extension EventBus: Eventable {}
发送通知:
scss
EventBus.userLogin.post()
响应式监听通知:
less
EventBus.userLogin.rx
.subscribe(onNext: { notification in
// 处理事件
})
.disposed(by: disposeBag)
如果需要更多业务隔离,书写方式可以这样:
dart
enum EventBus {
enum User: String {
case login
case logout
}
enum Other: String {
case hello
case byebye
}
}
extension EventBus.User: Eventable {}
extension EventBus.Other: Eventable {}
业务隔离的使用:
php
EventBus.User.login.rx().subscribe { notification in
print("rx() login")
guard let userInfo = notification.userInfo as? [String: String] else {
return
}
let name = userInfo["name"]
print(name)
}.disposed(by: rx.disposeBag)
EventBus.User.login.rx.subscribe { _ in
print("rx login")
}.disposed(by: rx.disposeBag)
EventBus.User.login.post(userInfo: ["name": "season"])
5. 加餐,思考Combine是否可行?
思考,既然RxSwift的这种思路可以支持这种写法,那么Swift原生的Combine,是否可行呢?
答案是:当然可以!
在Eventable协议中添加相关属性与方法:
swift
public protocol Eventable: RawRepresentable where RawValue == String {
func post(object: AnyObject?, userInfo: [AnyHashable: Any]?)
var publisher: AnyPublisher<Notification, Never> { get }
func publisher(object: AnyObject?) -> AnyPublisher<Notification, Never>
}
在Eventable中编写Combine的分类:
css
extension Eventable {
var publisher: AnyPublisher<Notification, Never> {
NotificationCenter.default
.publisher(for: Notification.Name(rawValue: rawValue))
.eraseToAnyPublisher()
}
func publisher(object: AnyObject? = nil) -> AnyPublisher<Notification, Never> {
NotificationCenter.default
.publisher(for: Notification.Name(rawValue: rawValue), object: object)
.eraseToAnyPublisher()
}
}
使用Combine进行监听:
swift
var cancellables = Set<AnyCancellable>()
EventBus.User.logout.publisher()
.sink { notification in
print("publisher() logout")
guard let userInfo = notification.userInfo as? [String: String] else {
return
}
let name = userInfo["name"]
print(name ?? "empty")
}
.store(in: &cancellables)
EventBus.User.logout.publisher
.sink { notification in
print("publisher logout")
}
.store(in: &cancellables)
EventBus.User.logout.post(userInfo: ["name": "zhu"])
总结
这个思路之所以能够实现类型安全、响应式、自动释放的通知机制,根本原因在于 RxSwift
对 NotificationCenter
的封装极大简化了观察者的生命周期管理。开发者只需专注于事件本身的业务逻辑,无需再为资源释放和内存安全担忧。
通过协议和扩展的方式,结合 RxSwift,把原本繁琐、易错的通知机制变得类型安全、响应式、易维护,非常适合中大型 Swift 项目,尤其是使用 RxSwift 的项目。提高的可维护性和开发效率。
推荐在需要模块解耦、事件驱动、响应式开发的场景下使用。
最后我们从RxSwift联想的Swift原生Combine框架,发现这种思路也是可行,也进一步说明,编程思路为导向的重要性。
文章引用
RxSwift:为何NotificationCenter无需管理观察者的生命周期?
iOS:NSNotification.Name从OC到Swift的写法演进