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

文档来源 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,全局单例。

csharp 复制代码
let center = NotificationCenter.default

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

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

php 复制代码
NotificationCenter.default.addObserver(self, selector:#selector(appEnterBg),
    name:UIApplication.didEnterBackgroundNotification, object:nil)

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


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

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

Swift 签名:

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 完整示例

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,selector需要
    @objc private func onLoginSuccess(_ notify: Notification) {
        //读取附带数据 userInfo
        if let dict = notify.userInfo as? [String:Any], let uid = dict["userId"] as? String {
            print("登录成功 userId=(uid)")
        }
    }
    
    // iOS9+:selector版本不需要手动removeObserver,对象销毁系统自动清理
    // deinit { NotificationCenter.default.removeObserver(self) }
}

适用场景

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

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

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

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

less 复制代码
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 示例

swift 复制代码
class ProfileVC: UIViewController {
    //保存token
    private var loginObserverToken: Any?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        loginObserverToken = NotificationCenter.default.addObserver(
            forName: .userLoginSuccess,
            object: nil,
            queue: .main) { [weak self] notify in
                // 直接闭包写逻辑,不用@objc函数
                guard let self = self else { return }
                print("Profile收到登录成功")
            }
    }
    
    deinit {
        // ✅必须手动移除!!block版本不会自动清理!!
        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,移除该对象全部监听
php 复制代码
NotificationCenter.default.removeObserver(self)

2. removeObserver(_:name:object:)

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

php 复制代码
NotificationCenter.default.removeObserver(self, name: .userLoginSuccess, object: nil)

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

arduino 复制代码
NotificationCenter.default.removeObserver(loginObserverToken!)

总结:

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

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

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

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

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

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

php 复制代码
NotificationCenter.default.post(name: .userLoginSuccess, object: self)

3. postNotification(_ notification: Notification)

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

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

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


NSDistributedNotificationCenter(文档提到)

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


真实业务场景对照

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

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

php 复制代码
//进入后台
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. 不要滥用通知,广播调试难,消息满天飞不好定位是谁发的。
相关推荐
末代iOS程序员华仔8 小时前
iOS个人开发者账号申请为啥会被卡住
ios·swift·figma
MaoJiu9 小时前
从纹理采样到局部形变:实时瘦鼻 Shader 算法解析
ios
奈斯先生Vector10 小时前
从“能调用”到“可替换”:Coze 多模型 API 编排层设计指南
linux·运维·人工智能·ubuntu·平面·ios
DogDaoDao10 小时前
HarmonyOS 深度解析:从微内核到 ArkTS 工程实战
android·linux·ios·华为·程序员·harmonyos·harmonyos next
Patrick_Wilson1 天前
iOS Safari Clipboard API 权限弹窗问题
前端·ios·safari
_瑞1 天前
APM_符号化原理
ios·汇编语言
花椒技术1 天前
不是跑通一次 Demo:花椒 QA 怎样把真机 UI 自动化做成回归流程
ios·单元测试·测试
2501_915918411 天前
Swift IDE 有哪些?按平台和需求选择Swift 开发工具
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
songgeb1 天前
UITableView 局部刷新 Crash:Invalid update
ios·架构