中文资讯 掘金 AI Fri, 28 Aug 2026 正文 ok 721 words

NSNotificationCenter(NotificationCenter)官方文档通俗解读、API 详解与 Swift 实战

英文标题:NSNotificationCenter(NotificationCenter)官方文档白话翻译 + API 详解 + Swift 实战

NotificationCenter 是 App 进程内的发布—订阅式消息分发机制,发送方无需知道接收方,多个观察者可订阅同一通知。文章梳理了 selector 与 block 两套观察者 API、移除观察者方式,以及携带 `object` 和 `userInfo` 的通知发送方法。selector 方式在 iOS 9 及以后会随观察者对象销毁自动移除,而 block 方式必须保存返回的 token 并手动移除,同时应使用 `[weak self]` 避免循环引用。对于开发者而言,通知适合系统事件和多模块广播同步,但不适合一对一通信或需要返回值的场景,并需注意回调线程和 `post` 的同步阻塞特性。
打开原文相关 2 · 机会 2
media3.2 KB media589 B media9.2 KB

中文

文档来源 Apple:NotificationCenter | Apple Developer Documentation

本质:发布—订阅(广播)机制。同一个 App 内部发送消息时,发送方无需知道谁会接收;接收方只要订阅消息即可收到。

⚠️ 只能在同一个 App 进程内通信;跨进程需要使用 NSDistributedNotificationCenter(仅 macOS 可用,iOS 不支持跨进程通知)。

总览翻译(Overview)

NotificationCenter 是一种消息分发机制,用于向已经注册的观察者广播消息。

  1. Objective-C / 旧 API:注册并接收 NSNotification 对象消息,有两种注册方式:

    • addObserver:selector:name:object: 方法:回调通过 @selector 函数执行。
    • addObserverForName:object:queue:usingBlock: 方法:回调通过 block 闭包执行。
  2. 新版 Swift 并发 API:addObserver(of:for:using:),具备类型安全特性,并适配 Swift Actor 隔离。

  3. 每个 App 都自带一个全局默认通知中心 defaultCenter;也可以自行创建独立的通知中心,用于分组消息。

  4. ❗普通通知只能在当前 App 内部收发。macOS 如需在不同程序之间发送消息,应使用 NSDistributedNotificationCenter;iOS 不具备跨进程通知能力。


核心 API 分为 4 大类

一、获取通知中心对象

1. defaultCenter(Swift:NotificationCenter.default)

作用:获取 App 全局默认通知中心,99% 的业务场景都使用它。

无需自行创建,这是一个全局单例。

let center = NotificationCenter.default

使用场景:监听 App 前后台状态、键盘弹出、登录成功广播、多页面同步刷新 UI。

前面监听 App 进入后台的写法:

NotificationCenter.default.addObserver(self, selector:#selector(appEnterBg),
    name:UIApplication.didEnterBackgroundNotification, object:nil)

这就是通过全局默认通知中心完成注册。


二、添加观察者(两套 API,重点区分常见问题)

API-1 addObserver(_:selector:name:object:):Objective-C 风格 selector 回调

Swift 签名:

func addObserver(_ observer: Any, 
    selector aSelector: Selector, 
    name aName: NSNotification.Name?, 
    object anObject: Any?)

参数说明:

参数 含义 传 nil 的效果
observer 谁要接收通知,一般传入 self 不能为 nil
selector 收到通知后执行的 @objc 方法 不能为 nil
name 通知名称,决定监听哪类消息 nil:监听全部通知
object 监听哪个发送者发出的通知 nil:接收所有发送者发出的该 name 通知

✅ Swift 完整示例:

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

class HomeVC: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        NotificationCenter.default.addObserver(self,
            selector: #selector(onLoginSuccess(_:)),
            name: .userLoginSuccess,
            object: nil)
    }
    
    
    @objc private func onLoginSuccess(_ notify: Notification) {
        
        if let dict = notify.userInfo as? [String:Any], let uid = dict["userId"] as? String {
            print("登录成功 userId=(uid)")
        }
    }
    
    
    
}

适用场景

  1. 控制器监听系统通知,例如进入后台、键盘弹出或收起。
  2. 简单广播,回调写在独立的 @objc 函数中,代码结构较清晰。

⚠️重要特性:iOS 9 之后,通过 selector 方式注册的观察者,在对象销毁时系统会自动移除,无需手动 remove;iOS 9 以前必须在 deinit 中移除,否则可能崩溃。

❗通知回调所在线程 = 发送通知所在线程。如果在子线程 post 通知,回调也会在子线程执行;更新 UI 时需要切换至主队列。

API-2 addObserver(forName:object:queue:using:):Block 闭包版本

该方法会返回一个 token 对象(Any NSObjectProtocol),必须保存 token,并手动 remove。

func addObserver(forName name: NSNotification.Name?, 
    object obj: Any?, 
    queue: OperationQueue?, 
    using block: @escaping (Notification) -> Void) -> Any

参数说明:

  • queue:指定 block 回调在哪个队列执行。传入 .main 时,回调会强制在主线程执行,适合更新 UI;传入 nil 时,与发送通知的线程保持一致。
  • block:收到通知后执行的闭包。
  • 返回值:observerToken,必须保存,并在销毁时 remove。

✅ Swift 示例:

class ProfileVC: UIViewController {
    
    private var loginObserverToken: Any?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        loginObserverToken = NotificationCenter.default.addObserver(
            forName: .userLoginSuccess,
            object: nil,
            queue: .main) { [weak self] notify in
                
                guard let self = self else { return }
                print("Profile收到登录成功")
            }
    }
    
    deinit {
        
        if let token = loginObserverToken {
            NotificationCenter.default.removeObserver(token)
        }
    }
}

核心踩坑点:

  1. block 版本不会自动移除观察者。即使 VC 已销毁,通知中心仍可能持有 token;不执行 remove 会留下残留观察者。
  2. block 内捕获 self 时必须使用 [weak self],否则可能形成循环引用并导致内存泄漏。
  3. queue 传入 .main 后,可以自动在主线程更新 UI,非常方便。

适用场景

  • 不想编写 @objc 函数;
  • 需要强制在主线程执行回调;
  • 希望直接在闭包中编写业务逻辑。

三、移除观察者 removeObserver

1. removeObserver(_ observer:)

Objective-C:- removeObserver:

  • selector 版本:传入注册时的 self,会移除该对象的全部监听。

    NotificationCenter.default.removeObserver(self)

2. removeObserver(_:name:object:)

仅移除指定 name、指定 sender 的监听,粒度更精确。

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

block 版本:传入方法返回的 token 对象进行移除,不要传 self!

NotificationCenter.default.removeObserver(loginObserverToken!)

总结:

  • selector 注册 → removeObserver 传入 self
  • block 注册 → removeObserver 传入返回的 token

四、发送通知 Post(广播消息)

1. postNotificationName:object:userInfo:(Swift:post(name:object:userInfo:))

最常用的发送方式,可以通过字典携带数据给接收方。

NotificationCenter.default.post(
    name: .userLoginSuccess,
    object: self,       // object:发送者是谁,接收方可以过滤sender
    userInfo: ["userId":"10086","userName":"张三"] // 传递附加数据 [AnyHashable:Any]
)
  • object:标记消息由谁发出。接收方在调用 addObserver 时,如果 object 填入该对象,就只会接收来自这个发送者的通知。
  • userInfo:用于传递附加参数,类型为 [AnyHashable: Any]。

2. postNotificationName:object:(不携带附加数据)

NotificationCenter.default.post(name: .userLoginSuccess, object: self)

3. postNotification(_ notification: Notification)

先手动构造 Notification 对象再发送,较少使用。

let notify = Notification(name: .userLoginSuccess, object:self, userInfo:["id":"123"])
NotificationCenter.default.post(notify)

✨执行特性:**post 是同步阻塞的。**发送通知时,系统会依次执行所有观察者回调,post 函数会在所有回调完成后才返回;如果某个观察者回调耗时较长,会阻塞发送方当前线程。


NSDistributedNotificationCenter(文档提到)

仅 macOS 可用,iOS 不能使用!

它的作用是实现跨进程通知,即同一台电脑上不同 App 进程之间广播消息。iOS 受沙盒隔离限制,不支持跨进程通知,无需考虑此 API。


真实业务场景对照

场景 1:App 切到后台、回到前台

系统提供了内置通知名称,可以直接监听:

NotificationCenter.default.addObserver(self, selector:#selector(didBg),
    name: UIApplication.didEnterBackgroundNotification, object:nil)

NotificationCenter.default.addObserver(self, selector:#selector(didFg),
    name: UIApplication.willEnterForegroundNotification, object:nil)

场景 2:登录成功后,首页和个人中心多个页面同时刷新 UI

  1. 登录模块登录成功后,post 通知并携带 userId;
  2. HomeVC、ProfileVC 订阅该通知,收到后刷新各自页面。

优势:登录模块无需持有 HomeVC、ProfileVC 等控制器,实现模块解耦。

场景 3:键盘弹出、收起时调整输入框位置

系统通知:UIResponder.keyboardWillShowNotification

❌不适合使用通知的场景

  1. 一对一通信:应优先使用 delegate 或闭包,不要滥用通知;
  2. 需要返回值:通知是单向广播,接收方无法向发送方返回结果。

selector 与 block 版通知对比表

项目 addObserver selector addObserver block(token)
回调形式 @objc 函数 闭包
是否自动释放 iOS 9+ 自动移除 必须手动 remove(token)
线程控制 回调线程 = 发送通知线程,需自行切换主线程 可通过 queue 参数直接指定 .main
循环引用风险 低 高,必须使用 [weak self]
推荐场景 系统通知、简单监听 需要主线程回调、不想写 @objc

常见踩坑清单

  1. block 版本忘记保存 token,或忘记 remove,导致残留观察者;
  2. 在子线程 post 通知后,回调会在子线程执行,直接更新 UI 可能崩溃;解决方式:注册时将 queue 设为 .main;
  3. userInfo 用于传递通知附加数据,需确保其中内容满足使用场景所需的类型和生命周期要求;
  4. post 是同步阻塞操作,某个观察者回调耗时过长会阻塞发送方;
  5. 不要滥用通知。广播链路难以调试,消息过多时不易定位具体发送方。

English / 原文

文档来源 Apple:NotificationCenter | Apple Developer Documentation 本质:发布‑订阅(广播)机制 ,同一个 App 内部发消息,发送方不用知道谁接收;接收方订阅消息即可收到。 ⚠️ 只能同一个 App 进程内通信 ;跨进程要用 NSDistributedNotificationCenter(仅 macOS 可用,iOS 不支持跨进程通知)。

总览翻译(Overview)

NotificationCenter 是消息分发机制,用来给已经注册的观察者广播消息。

  1. Objective‑C / 老 API:注册接收 NSNotification 对象消息,两个注册方式:

    • addObserver:selector:name:object: 方法:回调走 @selector 函数
    • addObserverForName:object:queue:usingBlock: 方法:回调走 block 闭包
  2. 新版 Swift 并发 API:addObserver(of:for:using:),类型安全、适配 Swift Actor 隔离。

  3. 每个 App 自带一个全局默认通知中心 defaultCenter;也可以自己新建独立通知中心做分组消息。

  4. ❗普通通知只能本 App 内部收发 。macOS 如果要不同程序之间发消息,要用 NSDistributedNotificationCenter,iOS 没有跨进程通知能力。


核心 API 分 4 大类

一、获取通知中心对象

1. defaultCenter(Swift:NotificationCenter.default)

作用:拿到 App 全局默认通知中心,99% 业务都用这个

不需要自己 new,全局单例。

let center = NotificationCenter.default

使用场景 :监听 App 前后台、键盘弹出、登录成功广播、多页面同步刷新 UI。

你前面写的监听 App 进入后台:

NotificationCenter.default.addObserver(self, selector:#selector(appEnterBg),
    name:UIApplication.didEnterBackgroundNotification, object:nil)

就是拿这个全局中心注册。


二、添加观察者(2 套 API,重点区分坑)

API‑1 addObserver(_:selector:name:object:) OC 风格 selector 回调

Swift 签名:

func addObserver(_ observer: Any, 
    selector aSelector: Selector, 
    name aName: NSNotification.Name?, 
    object anObject: Any?)

参数说明:

表格

参数 含义 传 nil 效果
observer 谁要接收通知,一般传self 不能 nil
selector 收到通知执行的 @objc 方法 不能 nil
name 通知名字,监听哪个消息 nil:监听全部通知
object 监听哪个发送者发出的通知 nil:接收所有发送者 的这条 name 通知

✅ Swift 完整示例

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

class HomeVC: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        NotificationCenter.default.addObserver(self,
            selector: #selector(onLoginSuccess(_:)),
            name: .userLoginSuccess,
            object: nil)
    }
    
    
    @objc private func onLoginSuccess(_ notify: Notification) {
        
        if let dict = notify.userInfo as? [String:Any], let uid = dict["userId"] as? String {
            print("登录成功 userId=(uid)")
        }
    }
    
    
    
}

适用场景

  1. 控制器监听系统通知(后台、键盘)
  2. 简单广播,回调写单独 @objc 函数,代码清晰。

⚠️重要特性: iOS9 之后,selector 方式注册,对象销毁系统自动移除观察者,不用手动 remove ;iOS9 以前必须 deinit 移除否则崩溃。 ❗通知回调线程 = 发送通知所在线程 !如果子线程 post 通知,回调也跑在子线程,更新 UI 要切主队列。

API‑2 addObserver(forName:object:queue:using:) Block 闭包版本

返回一个 token 对象(Any NSObjectProtocol),必须保存 token,手动 remove!!

func addObserver(forName name: NSNotification.Name?, 
    object obj: Any?, 
    queue: OperationQueue?, 
    using block: @escaping (Notification) -> Void) -> Any

参数:

  • queue:block 回调在哪个队列执行;传 .main → 回调强制主线程(更新 UI 首选);nil = 和发送通知同一个线程。
  • block:收到通知执行闭包。
  • 返回值:observerToken,必须存起来,销毁时 remove!!

✅ Swift 示例

class ProfileVC: UIViewController {
    
    private var loginObserverToken: Any?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        loginObserverToken = NotificationCenter.default.addObserver(
            forName: .userLoginSuccess,
            object: nil,
            queue: .main) { [weak self] notify in
                
                guard let self = self else { return }
                print("Profile收到登录成功")
            }
    }
    
    deinit {
        
        if let token = loginObserverToken {
            NotificationCenter.default.removeObserver(token)
        }
    }
}

坑点重中之重 博客园:

  1. block 版本不会自动移除观察者 ,哪怕 vc 销毁,通知中心仍然持有 token 对象,不 remove 会残留;
  2. block 内部捕获 self 一定要写[weak self],否则循环引用内存泄漏;
  3. queue 传 .main,自动切主线程做 UI,非常方便。

适用场景

  • 不想写@objc函数;
  • 需要强制回调主线程;
  • 闭包内直接写业务逻辑。

三、移除观察者 removeObserver

1. removeObserver(_ observer:)

OC:‑ removeObserver:

  • selector 版本:传入当初注册的self,移除该对象全部监听 。

    NotificationCenter.default.removeObserver(self)

2. removeObserver(_:name:object:)

只移除指定 name、指定 sender 的监听,更精准。

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

block 版本:传入返回的 token 对象 移除,不要传 self!

NotificationCenter.default.removeObserver(loginObserverToken!)

总结:

  • selector 注册 → removeObserver 传 self
  • block 注册 → removeObserver 传返回的 token

四、发送通知 Post(广播出去)

1. postNotificationName:object:userInfo:(Swift:post(name:object:userInfo:))

最常用,可以附带字典数据传给接收方。

NotificationCenter.default.post(
    name: .userLoginSuccess,
    object: self,       // object:发送者是谁,接收方可以过滤sender
    userInfo: ["userId":"10086","userName":"张三"] // 传递附加数据 [AnyHashable:Any]
)
  • object:标记消息是谁发出来;接收方 addObserver 的时候 object 填这个对象,就只会接收来自这个发送者的通知。
  • userInfo:传递参数,只能存引用类型,不能直接放 struct 结构体。

2. postNotificationName:object:(不带附加数据)

NotificationCenter.default.post(name: .userLoginSuccess, object: self)

3. postNotification(_ notification: Notification)

先手动构造 Notification 对象再发送,很少用。

let notify = Notification(name: .userLoginSuccess, object:self, userInfo:["id":"123"])
NotificationCenter.default.post(notify)

✨执行特性:post 是同步阻塞 !发送通知的时候,会挨个执行完所有观察者回调,post 函数才返回;如果某个观察者回调耗时很大,会卡住发送方当前线程。


NSDistributedNotificationCenter(文档提到)

仅 macOS 可用,iOS 不能用! 作用:跨进程通知 ,一台电脑上不同 App 进程之间广播消息。 iOS 沙盒隔离,不支持跨进程通知,不用管。


真实业务场景对照

场景 1:App 切后台、切前台(你上一个问题)

系统自带通知名字,直接监听

NotificationCenter.default.addObserver(self, selector:#selector(didBg),
    name: UIApplication.didEnterBackgroundNotification, object:nil)

NotificationCenter.default.addObserver(self, selector:#selector(didFg),
    name: UIApplication.willEnterForegroundNotification, object:nil)

场景 2:登录成功,首页、个人中心多个页面同时刷新 UI

  1. 登录模块登录成功 → post 通知带上 userId;
  2. HomeVC、ProfileVC 订阅这个通知,收到刷新页面。

优势:登录模块不需要持有 Home、Profile 控制器,解耦。

场景 3:键盘弹出收起,调整输入框位置

系统通知:UIResponder.keyboardWillShowNotification

❌不适合用通知场景

  1. 一对一通信:优先用 delegate、闭包,不要滥用通知;
  2. 需要返回值:通知是单向广播,接收方无法给发送方返回结果。

selector vs block 版通知对比表

表格

项目 addObserver selector addObserver block(token)
回调形式 @objc 函数 闭包
是否自动释放 iOS9 + 自动移除 必须手动 remove (token)
线程控制 回调 = 发送通知线程,自己切主线程 queue 参数可以直接指定.main
循环引用风险 低 高,必须[weak self]
推荐场景 系统通知、简单监听 需要主线程回调,不想写 @objc

常见踩坑清单

  1. block 版本忘记保存 token,忘记 remove,残留观察者;
  2. 子线程 post 通知,回调跑在子线程,直接更新 UI 崩溃;解决:注册 queue 传.main;
  3. userInfo 不能直接传 struct,要包装成 class 对象;
  4. post 同步阻塞,某个观察者回调卡死会卡住发送方;
  5. 不要滥用通知,广播调试难,消息满天飞不好定位是谁发的。