[翻译]NotificationCenter in Swift

本文的原地址是:NotificiationCenter。我对原文做了一些修改,删除了一些冗杂的部分。


A notification dispatch mechanism that enables the broadcast of information to registered observers

Source - Apple Documents

通知分发机制使得信息可以广播给所有注册的观测者。

1 在开始之前我们来看看为什么我们需要这个机制

在移动应用开发中有时候我们需要实现一些诸如处理手机旋转,从一个类向另一个类传递数据,调用任意一个其他函数等等这些问题。一般来说代理模式比较适合两个类之间的通信问题,但是代理模式 (Delegation pattern) 无法实现消息的广播。

代理模式的另一个问题是强耦合性。即我们在消息源中要显式地定义代理。还有一个问题是代理模式需要明确的传递路径,那么在复杂的对象结构中,我们需要传递消息的话可能需要经过冗长的调用过程。

2 如何使用

2.1 接收端

1
2
3
4
5
6
NotificationCenter.default
.addObserver(
self,
selector: #selector(loginSuccess),
name: NSNotification.Name("com.user.login.success"),
object: nil)

上面是一个典型的观测者注册过程。消息传递的中枢是 NotificationCenter。消息的辨识符是 NSnotification 对象,这个对象的名称应该是唯一的。由于你是用的第三方库中也可能也使用了 NotificationCenter,如果你选择的名字比较简单(例如在这里只写 'login.success'),那么有可能和第三方库冲突。因此一般会选择一个类似 bundle ID 的前缀。addObserver最后一个参数 object 是传递给消息的接受函数的对象,即这里的 loginSuccess 函数的输入参数

这里的 object 起到一个筛选作用,用来指明消息的发送者。当不为 nil 时,Observer 只会接受来自这个对象的消息。

2.2 发送消息

将消息发送给所有的观测者方法是

1
2
3
4
NotificationCenter.default
.post(name:
NSNotification.Name("com.user.login.success"),
object: nil)

这里的 object 参数用来设定消息的发送者,同 addObserver 函数中的 object 参数配合完成对消息发送源的筛选处理。

3 完整的流程

3.1 Step 1: 实现消息处理函数

1
2
3
4
@objc
func loginSuccess(_ notification: Notification) {
// do something
}

3.2 Step 2: 注册观测者

1
2
3
4
5
6
NotificationCenter.default
.addObserver(self,
selector:#selector(loginSuccess(_:)),
name: NSNotification.Name
("com.user.login.success"),
object: nil)

3.3 Step 3: 发布消息

1
2
3
4
5
6
let loginResponse = ["userInfo": ["userID": 6, "userName": "John"]]
NotificationCenter.default
.post(name:
NSNotification.Name("com.user.login.success"),
object: nil,
userInfo: loginResponse)

注意这里使用了 userInfo 来传递数据。

3.4 处理消息发送端传递来的数据

1
2
3
4
5
@objc func loginSuccess(_ notification: Notification) {
print(notification.userInfo?["userInfo"] as? [String: Any] ?? [:])
}
------------------------------------------
Output - ["userID": 6, "userName": "John"]

3.5 移除消息监听

1
2
3
4
deinit {
NotificationCenter.default
.removeObserver(self, name:
NSNotification.Name("com.user.login.success"), object: nil) }

deinit 用来在一个对象被 deallocated 之后执行一些清理操作。在这里来说,是确保对象析构的时候,移除对应的监听者,避免NotificationCenter对于监听者对象的持有导致对象无法被正确析构。不过在 iOS 9 以后,系统会自动完成对于 Observer 的清理,无需再像这样进行手动清理。对应的文档:removeObserver(_:)


在实际使用中我们要避免在代码中直接写 'com.user.login.success' 这种固定的字符串,偶然的拼写错误可能导致难以理解的 bug。另外,如果我们要修改消息的命名,工作量也会相对较大。因此最好的方法是使用 Swift 的扩展机制

1
2
3
extension Notification.Name {
static var loginSuccess: Notification.Name {
return .init(rawValue: "UserLogin.success") }