1. 什么是 NotificationCenter? #
NotificationCenter
是 iOS/macOS 中的一个 发布/订阅通知机制,用于在应用程序运行时实现不同对象之间的松耦合通信。通过 NotificationCenter
,您无需直接引用目标对象,而是通过添加观察者、发布通知的方式,向全局共享的 NotificationCenter
注册观察者,并当广播通知时触发相应的操作。
主要特点 #
- 发布/订阅模式: 允许对象通过订阅接收事件(通知);发布者不需要知道谁会处理通知,处理者也不需要知道谁发送了通知。
- 松耦合:
两个对象间无需直接交互,可以完全独立,只通过
NotificationCenter
作为中间人进行沟通。 - 全局共享实例:
系统提供了一个默认的共享实例
NotificationCenter.default
,大多数情况下可以直接使用。
2. NotificationCenter 的组成与体系结构 #
NotificationCenter
的体系结构由以下部分组成:
2.1 通知中心(NotificationCenter) #
这是核心部分,负责管理通知机制的工作:
- 添加和移除观察者(监听者);
- 发出(广播)通知给观察者。
API 入口:
NotificationCenter.default
为共享实例,全局可用。- 你也可以通过
NotificationCenter()
创建自己的实例来实现独立的通知机制。
2.2 通知(Notification) #
封装了事件的对象,它是一个 Notification
类型的结构,包含以下内容:
- 通知的
name
(标识事件)的唯一标志(Notification.Name
类型)。 - 一个可选的
userInfo
(键值对)字典,用于传递附加信息。 - 通知发送者(
object
),可以指定触发通知的对象。
创建通知:
通知本质是通过 Notification
对象封装的:
let notification = Notification(name: .someNotificationName, object: someObject, userInfo: ["key": "value"])
2.3 通知名字(Notification.Name) #
通知的标识符,类型为 Notification.Name
,表示一个通知事件的名称。推荐使用扩展来定义所有通知名称,集中管理。
extension Notification.Name {
static let userDidLogin = Notification.Name("userDidLogin")
static let dataDidUpdate = Notification.Name("dataDidUpdate")
}
3. 如何使用 NotificationCenter #
3.1 基本步骤: #
NotificationCenter
的使用一般分以下三步:
定义通知名称:
- 使用
Notification.Name
定义一个全局的通知名称,便于发布者和观察者都知道事件名称。
extension Notification.Name { static let someNotification = Notification.Name("someNotification") }
- 使用
监听通知(订阅者):
- 在需要接收通知的对象中,添加监听器,用
NotificationCenter.default.addObserver()
注册感兴趣的通知。
NotificationCenter.default.addObserver(self, selector: #selector(handleNotification(_:)), name: .someNotification, object: nil)
- 监听器
selector
方法使用@objc
修饰,并接受一个Notification
参数:@objc func handleNotification(_ notification: Notification) { print("Notification received: \(notification)") }
- 在需要接收通知的对象中,添加监听器,用
发送通知(发布者):
- 在需要触发事件的地方,通过
NotificationCenter.default.post()
广播通知。
NotificationCenter.default.post(name: .someNotification, object: nil, userInfo: ["key": "value"])
- 在需要触发事件的地方,通过
3.2 使用示例 #
案例:用户登录事件广播
- 定义通知名称
extension Notification.Name {
static let userDidLogin = Notification.Name("userDidLogin")
}
- 发布者:广播通知
class LoginManager {
func loginUser() {
print("User has logged in!")
// 发布通知
NotificationCenter.default.post(name: .userDidLogin, object: self, userInfo: ["username": "JohnSmith"])
}
}
- 观察者:接收并响应通知
class HomePageViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 添加用户登录通知监听
NotificationCenter.default.addObserver(self,
selector: #selector(handleUserLogin(_:)),
name: .userDidLogin,
object: nil)
}
@objc func handleUserLogin(_ notification: Notification) {
if let userInfo = notification.userInfo,
let username = userInfo["username"] as? String {
print("User logged in: \(username)")
}
}
// 一定记得移除监听,防止内存泄漏
deinit {
NotificationCenter.default.removeObserver(self, name: .userDidLogin, object: nil)
}
}
4. 通知中心的高级功能 #
4.1 过滤特定通知发送者 #
- 可以设置
object
来过滤特定对象的通知。 - 当
addObserver
与post
的object
匹配时,通知才会发送。
NotificationCenter.default.addObserver(self,
selector: #selector(handleNotification(_:)),
name: .someNotification,
object: specificObject) // 仅接收 `specificObject` 发送的通知
发送方:
NotificationCenter.default.post(name: .someNotification, object: specificObject)
4.2 手动移除通知观察者 #
- 在不再需要接收通知时,手动移除观察者:
NotificationCenter.default.removeObserver(self, name: .someNotification, object: nil)
- 如果不指定
name
或object
,会移除监听的所有通知:
NotificationCenter.default.removeObserver(self)
4.3 使用 Combine 替代 Observer #
在 iOS 13+ 中,Combine
框架提供了一种更现代的声明式方法,替代 addObserver
。
import Combine
class HomePageViewController: UIViewController {
var cancellables: [AnyCancellable] = []
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.publisher(for: .userDidLogin)
.sink { notification in
if let userInfo = notification.userInfo,
let username = userInfo["username"] as? String {
print("User logged in: \(username)")
}
}
.store(in: &cancellables)
}
}
5. 什么时候使用 NotificationCenter? #
合适场景 #
对象间松耦合通信: 当多个对象需要相互通信,但彼此不应直接引用(强耦合)时,
NotificationCenter
是一个强大的工具。例如:- 全局事件通知:
- 用户登录、注销、数据更新。
- 模块间通信:
将通知作为模块间传递数据与事件的桥梁,比如
ViewController
与后台数据模块之间通信。
- 全局事件通知:
事件广播/全局通知:
- 一次性向多个对象发送消息。
- 比如,当主题或语言发生更改时,更新整个应用的 UI 样式。
响应特定系统事件: 系统使用
NotificationCenter
广播了一些重要事件,比如:- 键盘事件:
NSNotification.Name.UIKeyboardWillShow
、UIKeyboardDidHide
。 - 设备旋转事件:
UIDevice.orientationDidChangeNotification
。
- 键盘事件:
数据更新事件: 当数据模型或配置发生变化时,通知所有依赖它的模块或页面刷新数据。
不适用场景 #
需要保证事件响应顺序:
NotificationCenter
不保证通知是按顺序到达的,如果需要严格的顺序控制,使用 回调函数 或 代理模式 更适合。单点对单点通信: 如果只有一个对象需要知道事件的发生,使用 闭包 或 Delegate(代理模式) 会更清晰。
跨进程通知: 需要用于多进程间通信时,可考虑使用其他机制。
6. 常见注意事项 #
移除观察者内存泄漏:
- 使用
NotificationCenter.default.addObserver
后,必须在适当时机移除监听,否则会造成内存泄漏。 - 尤其是在
deinit
中确保观察者被移除:deinit { NotificationCenter.default.removeObserver(self) }
- 使用
避免名字冲突: 使用
Notification.Name
扩展来避免不同模块使用相同的通知名字导致冲突。线程安全:
NotificationCenter
的所有功能是线程安全的,但通知接收者的代码必须自己确保在主线程上更新 UI:DispatchQueue.main.async { // 更新 UI 逻辑 }
总结 #
NotificationCenter
优点:
- 简单、灵活,适合多对象解耦。
- 广播机制强大,支持全局与特定事件的监听。
- 系统级通知支持丰富。
典型使用场景:
- 全局事件广播:如登录事件、数据刷新。
- 轻量级模块间通信:不需要严格绑定对象。
其他选择: 对于现代 Swift 项目,可以结合 Combine 框架 或替代设计模式(如 Delegate/闭包/回调函数)来提升代码的可读性和可靠性。