iOS通知机制NotificationCenter
iOS通知机制NotificationCenter

iOS通知机制NotificationCenter

NotificationCenter 是 iOS/macOS 开发中的发布-订阅模式(Publish-Subscribe)的通知机制,用于在应用内的不同部分之间传递消息。

NotificationCenter作用

让不同的对象进行通信,而不需要直接引用对方(解耦)。

一个对象发送通知,多个对象可以接收并作出反应。

适用于监听系统级别或自定义的事件(如键盘弹起、iCloud 数据变化等)。

NotificationCenter 的核心概念

1、Notification

通知(Notification)就是一条消息,比如:

“iCloud 数据发生变化了”

“用户登录成功了”

“键盘弹出来了”

“App 进入后台”

2、Observer(监听者)

监听者(Observer)可以监听特定的通知,当通知发布时,监听者会执行相应的方法。

3、Post(发送通知)

发布者(Post)可以向 NotificationCenter 发送通知,通知会被所有监听它的对象接收。

NotificationCenter常用形式

1、添加观察者(监听通知)

Block 形式(更现代、类型安全)
NotificationCenter.default.addObserver(
    forName: NSNotification.Name?,
    object: Any?,
    queue: OperationQueue?,
    using: @escaping (Notification) -> Void
)

参数解释

1、forName:类型Notification.Name?,要监听的通知名,比如 .NSManagedObjectContextDidSave。

2、object:类型Any?,限制通知来源对象(发通知的对象)。设为 nil 表示监听所有来源。

3、queue:类型OperationQueue?,指定处理通知的 block 运行在哪个队列上:nil = 发通知所在的线程.main = 主线程。

4、using:类型(Notification) -> Void,通知到来时要执行的代码。

代码示例

NotificationCenter.default.addObserver(
    forName: .NSManagedObjectContextDidSave,
    object: nil,
    queue: .main
) { notification in
    // 在主线程中处理 Core Data 更新
    viewContext.perform {
        viewContext.mergeChanges(fromContextDidSave: notification)
    }
}

特点

1、没有 selector,不需要在类里写方法。

2、使用 Swift 闭包,线程控制灵活、安全。

3、易于移除:需要保存返回的 NSObjectProtocol 实例再调用 removeObserver.

传统 Selector 形式(OC风格)
NotificationCenter.default.addObserver(
    _ observer: Any,
    selector: Selector,
    name: NSNotification.Name?,
    object: Any?
)

参数解释

1、observer:类型Any,谁来响应通知(通常是 self)。

2、selector:类型Selector,收到通知后执行的方法名。

3、name:类型Notification.Name?,通知名。

4、object:类型Any?,通知的来源。

代码示例

NotificationCenter.default.addObserver(
    self,
    selector: #selector(handleNotification(_:)),
    name: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
    object: NSUbiquitousKeyValueStore.default
)

@objc func handleNotification(_ notification: Notification) {
    // 处理逻辑
}

特点

1、需要写带 @objc 的方法(兼容 Objective-C)。

2、无法指定在哪个线程执行,和通知发出者线程保持一致。

3、通常用于需要在 NSObject 子类中响应系统通知,如 AppDelegate、UIViewController 等。

2、发送通知

NotificationCenter.default.post(…) 用于主动发送通知消息,它有几种不同的用法,适合在 App 内部不同模块之间传递信息,比如数据更新、用户登录状态变化、任务完成等。

NotificationCenter.default.post(
    name: .someNotificationName,
    object: self, // 发送者
    userInfo: ["key": "value"] // 附加数据
)

name:通知名称(建议用扩展定义 Notification.Name)

object:可选。通知来源,一般为 self,或用于过滤接收来源。

userInfo 是 [AnyHashable: Any]? 类型的字典,用于传递更多上下文信息。

监听方通过 notification.userInfo 读取这些数据。

1、通知名扩展定义(推荐写法)

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

2、发送通知

NotificationCenter.default.post(
    name: .userDidLogin,
    object: nil,
    userInfo: ["userId": "1234", "name": "Alice"]
)

3、接收通知(Swift block 写法)

NotificationCenter.default.addObserver(
    forName: .userDidLogin,
    object: nil,
    queue: .main
) { notification in
    if let info = notification.userInfo as? [String: Any],
       let name = info["name"] as? String {
        print("欢迎 \(name)")
    }
}

还可以设置 object 过滤通知来源:

NotificationCenter.default.addObserver(
    forName: .myNotificationName,
    object: myManager, // 只监听 myManager 发出的通知
    queue: .main
) { ... }

3、移除观察者

Block形式移除监听的方式

let token = NotificationCenter.default.addObserver(...)
NotificationCenter.default.removeObserver(token)

需要保存 token: NSObjectProtocol 变量。

传统 Selector 形式移除监听

NotificationCenter.default.removeObserver(self, name: ..., object: ...)

在 Swift 项目中建议尽量使用 Block 形式,特别是 ViewModel、异步数据处理等地方。

只有在必须响应系统通知且不想写闭包时,才考虑 Selector 形式。

监听iCloud数据变化

1、监听通知(添加 Observer)

在代码中,可以监听(addObserver)特定的通知,比如 iCloud 数据变化:

NotificationCenter.default.addObserver(
    self, 
    selector: #selector(handleNotification(_:)), 
    name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, 
    object: NSUbiquitousKeyValueStore.default
)

NotificationCenter.default.addObserver(…) → 监听特定通知。

selector: #selector(handleNotification(_:)) → 当收到通知时,执行 handleNotification(_:) 方法。

name: NSUbiquitousKeyValueStore.didChangeExternallyNotification → 监听 iCloud 数据变化。

object: NSUbiquitousKeyValueStore.default → 监听 iCloud KVS(Key-Value Store) 的变化。

2、处理通知

当 NotificationCenter 收到通知后,会执行 handleNotification(_:) 方法:

@objc private func handleNotification(_ notification: Notification) {
    print("收到 iCloud 数据变化通知!")
}

3、发送通知

某些时候,可以主动发送通知:

NotificationCenter.default.post(
    name: Notification.Name("MyCustomNotification"), 
    object: nil
)

这样,所有监听 MyCustomNotification 的对象都会收到通知。

4、移除监听

在 deinit 中移除监听,避免内存泄漏:

deinit {
    NotificationCenter.default.removeObserver(self)
}

实际应用

监听iCloud和应用生命周期事件,调用对应的方法。

/// 监听 iCloud 变化,同步到本地
private func observeiCloudChanges() {
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(iCloudDidUpdate),
        name: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
        object: NSUbiquitousKeyValueStore.default
    )
}

/// iCloud 数据变化时,更新本地数据
@objc private func iCloudDidUpdate(notification: Notification) {
    loadFromiCloud()
}

/// 监听应用生命周期事件
private func observeAppLifecycle() {
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(appWillResignActive),
        name: UIApplication.willResignActiveNotification,
        object: nil
    )
}

/// 当应用进入后台时,将数据同步到 iCloud
@objc private func appWillResignActive() {
    syncToiCloud()
}

当iCloud发生变化时,调用iCloudDidUpdate方法,该方法会执行loadFromiCloud方法(该方法表示将iCloud数据同步到本地,在此处省略)。

当应用生命周期发生变化时,同样会调用appDidEnterBackground,该方法也会调用syncToiCloud方法,将数据同步到iCloud。

在类的构造方法中,调用这两个方法,实现监听:

@Observable
class AppStorageManager {
    static let shared = AppStorageManager()  // 全局单例
    private init() {
        // 监听 iCloud 变化,同步到本地
        observeiCloudChanges()
        
        // 监听应用进入后台事件
        observeAppLifecycle()
    }
}

在构造方法中运行对应的NotificationCenter方法,从而实现全局事件的监听。

总结

NotificationCenter 适用于全局事件监听,比如:

1、iCloud 数据变化。

2、用户登录状态变化。

3、App 进入后台或前台。

4、设备旋转、键盘弹起等系统事件。

可以把它理解成广播机制,多个对象可以订阅同一个事件,当事件发生时,所有订阅者都会收到通知。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注