在 Objective-C 中,NSNotificationCenter(通知中心) 是基于观察者模式实现的跨对象通信机制,用于解耦不同组件间的消息传递。要理解它的内部工作原理,以及自己如何实现一个简易版通知中心,我们可以从底层原理和自定义实现两方面展开分析。
一、OC 原生通知中心(NSNotificationCenter)的内部工作原理
NSNotificationCenter 的核心是管理观察者的注册、通知的发送与分发,其内部主要分为主线程通知中心和全局通知中心(+defaultCenter),核心逻辑包括以下几个关键环节:
1. 观察者注册(addObserver 相关方法)
当调用 addObserver:selector:name:object: 或 addObserverForName:object:queue:usingBlock: 时,通知中心会做这些事:
- 存储观察者信息:内部维护一个哈希表/字典,以通知名称(name) 和发送者(object) 为键,存储对应的观察者列表(包含观察者对象、响应方法/Block、队列等信息)。
- 处理重复注册:默认允许同一观察者对同一通知重复注册(会多次响应),需开发者自行保证唯一性。
- 弱引用观察者:为了避免循环引用,通知中心对观察者对象持弱引用(ARC 下),如果观察者被释放,通知中心不会保留它(但旧版本手动管理内存时需手动移除观察者)。
2. 发送通知(postNotification 相关方法)
当调用 postNotification: 或 postNotificationName:object:userInfo: 时,通知中心会执行:
- 匹配观察者:根据通知名称(name)和发送者(object),从哈希表中查找对应的所有观察者。
-
分发通知:
- 如果是同步发送:直接在当前线程调用观察者的响应方法/Block。
- 如果是异步发送(通过
addObserverForName:object:queue:usingBlock:指定队列):将通知分发任务提交到指定的 GCD 队列中执行。
-
处理 nil 匹配:如果通知名称为
nil,会匹配所有名称的通知;如果发送者为nil,会匹配所有发送者的通知。
3. 移除观察者(removeObserver 相关方法)
当调用 removeObserver: 或 removeObserver:name:object: 时,通知中心会:
- 清理观察者列表:从哈希表中移除指定观察者的相关注册信息,避免野指针调用。
- 自动移除(ARC 优化):在 iOS 9 / macOS 10.11 之后,通知中心会自动对已释放的观察者做清理,无需手动移除(但建议显式移除以规范)。
4. 特殊的通知队列(NSNotificationQueue)
NSNotificationQueue 是通知的缓冲队列,基于 RunLoop 实现,用于批量处理通知:
- 可设置合并策略(如同一通知合并为一次)。
- 延迟发送通知,避免短时间内大量通知导致的性能问题。
5. 线程特性
- 通知的分发线程由发送线程决定(同步发送时),如果需要在主线程处理,需在观察者的响应方法中手动切线程。
- Block 方式注册时可指定队列,通知会在指定队列中执行,无需手动切线程。
二、自定义实现一个简易的 OC 通知中心
如果要自己实现一个通知中心,核心需要实现观察者注册、通知发送、观察者移除三大功能,同时要解决弱引用、线程安全、匹配规则等问题。以下是分步实现思路和代码示例:
设计思路
-
数据结构:用字典存储观察者信息,键为
通知名称+发送者的组合,值为观察者数组。 - 观察者模型:封装观察者的对象、响应方法/Block、队列等信息,避免直接存储强引用。
-
线程安全:使用锁(
NSLock/dispatch_semaphore_t)保证多线程下的读写安全。 - 弱引用处理:对观察者对象使用弱引用,避免循环引用。
- 通知分发:支持同步分发,可选异步分发(基于 GCD 队列)。
代码实现
1. 定义观察者模型(HYBObserver.h/m)
用于封装观察者的相关信息(弱引用观察者、响应方法、Block、队列等)。
// HYBObserver.h
#import <Foundation/Foundation.h>
@class HYBNotification;
typedef void (^HYBNotificationBlock)(HYBNotification *notification);
@interface HYBObserver : NSObject
/// 弱引用观察者对象
@property (nonatomic, weak) id observer;
/// 响应方法(SEL)
@property (nonatomic, assign) SEL selector;
/// 响应 Block
@property (nonatomic, copy) HYBNotificationBlock block;
/// 执行队列(Block 方式时使用)
@property (nonatomic, strong) dispatch_queue_t queue;
/// 通知名称(用于快速匹配)
@property (nonatomic, copy) NSString *name;
/// 发送者(用于快速匹配)
@property (nonatomic, weak) id object;
/// 初始化 SEL 类型的观察者
+ (instancetype)observerWithObserver:(id)observer
selector:(SEL)selector
name:(NSString *)name
object:(id)object;
/// 初始化 Block 类型的观察者
+ (instancetype)observerWithObserver:(id)observer
block:(HYBNotificationBlock)block
queue:(dispatch_queue_t)queue
name:(NSString *)name
object:(id)object;
@end
// HYBObserver.m
#import "HYBObserver.h"
@implementation HYBObserver
+ (instancetype)observerWithObserver:(id)observer
selector:(SEL)selector
name:(NSString *)name
object:(id)object {
HYBObserver *obs = [[self alloc] init];
obs.observer = observer;
obs.selector = selector;
obs.name = name;
obs.object = object;
return obs;
}
+ (instancetype)observerWithObserver:(id)observer
block:(HYBNotificationBlock)block
queue:(dispatch_queue_t)queue
name:(NSString *)name
object:(id)object {
HYBObserver *obs = [[self alloc] init];
obs.observer = observer;
obs.block = block;
obs.queue = queue ?: dispatch_get_main_queue(); // 默认主队列
obs.name = name;
obs.object = object;
return obs;
}
@end
2. 定义通知模型(HYBNotification.h/m)
模仿 NSNotification,封装通知的名称、发送者、附加信息。
// HYBNotification.h
#import <Foundation/Foundation.h>
@interface HYBNotification : NSObject
/// 通知名称
@property (nonatomic, copy, readonly) NSString *name;
/// 发送者
@property (nonatomic, strong, readonly) id object;
/// 附加信息
@property (nonatomic, strong, readonly) NSDictionary *userInfo;
/// 初始化通知
+ (instancetype)notificationWithName:(NSString *)name
object:(id)object
userInfo:(NSDictionary *)userInfo;
@end
// HYBNotification.m
#import "HYBNotification.h"
@implementation HYBNotification
+ (instancetype)notificationWithName:(NSString *)name
object:(id)object
userInfo:(NSDictionary *)userInfo {
HYBNotification *notification = [[self alloc] init];
_name = name;
_object = object;
_userInfo = userInfo ?: @{};
return notification;
}
@end
3. 实现通知中心核心类(HYBNotificationCenter.h/m)
单例设计,实现注册、发送、移除观察者的核心逻辑,并保证线程安全。
// HYBNotificationCenter.h
#import <Foundation/Foundation.h>
#import "HYBNotification.h"
#import "HYBObserver.h"
@interface HYBNotificationCenter : NSObject
/// 获取默认通知中心(单例)
+ (instancetype)defaultCenter;
/// 注册 SEL 类型的观察者
- (void)addObserver:(id)observer
selector:(SEL)selector
name:(NSString *)name
object:(id)object;
/// 注册 Block 类型的观察者
- (id)addObserverForName:(NSString *)name
object:(id)object
queue:(dispatch_queue_t)queue
usingBlock:(HYBNotificationBlock)block;
/// 发送通知
- (void)postNotification:(HYBNotification *)notification;
- (void)postNotificationName:(NSString *)name
object:(id)object;
- (void)postNotificationName:(NSString *)name
object:(id)object
userInfo:(NSDictionary *)userInfo;
/// 移除观察者
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer
name:(NSString *)name
object:(id)object;
@end
// HYBNotificationCenter.m
#import "HYBNotificationCenter.h"
#import <objc/message.h>
@interface HYBNotificationCenter ()
/// 存储观察者:key = 通知名称,value = 观察者数组(HYBObserver)
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSMutableArray<HYBObserver *> *> *observerDict;
/// 线程安全锁
@property (nonatomic, strong) NSLock *lock;
/// Block 观察者的持有者(用于返回给外部,供移除)
@property (nonatomic, strong) NSMutableSet *blockObservers;
@end
@implementation HYBNotificationCenter
+ (instancetype)defaultCenter {
static HYBNotificationCenter *_defaultCenter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_defaultCenter = [[self alloc] init];
});
return _defaultCenter;
}
- (instancetype)init {
if (self = [super init]) {
_observerDict = [NSMutableDictionary dictionary];
_lock = [[NSLock alloc] init];
_blockObservers = [NSMutableSet set];
}
return self;
}
#pragma mark - 注册观察者
- (void)addObserver:(id)observer
selector:(SEL)selector
name:(NSString *)name
object:(id)object {
if (!observer || !selector || !name) return;
[self.lock lock];
// 创建观察者模型
HYBObserver *hybObserver = [HYBObserver observerWithObserver:observer
selector:selector
name:name
object:object];
// 从字典中获取对应名称的观察者数组,不存在则创建
NSMutableArray *observers = self.observerDict[name];
if (!observers) {
observers = [NSMutableArray array];
self.observerDict[name] = observers;
}
[observers addObject:hybObserver];
[self.lock unlock];
}
- (id)addObserverForName:(NSString *)name
object:(id)object
queue:(dispatch_queue_t)queue
usingBlock:(HYBNotificationBlock)block {
if (!name || !block) return nil;
[self.lock lock];
// 创建 Block 类型的观察者(观察者对象为 NSObject 占位,因为 Block 无需外部观察者)
id placeholderObserver = [[NSObject alloc] init];
HYBObserver *hybObserver = [HYBObserver observerWithObserver:placeholderObserver
block:block
queue:queue
name:name
object:object];
// 存储观察者
NSMutableArray *observers = self.observerDict[name];
if (!observers) {
observers = [NSMutableArray array];
self.observerDict[name] = observers;
}
[observers addObject:hybObserver];
[self.blockObservers addObject:placeholderObserver];
[self.lock unlock];
return placeholderObserver; // 返回占位对象,供外部移除
}
#pragma mark - 发送通知
- (void)postNotification:(HYBNotification *)notification {
if (!notification || !notification.name) return;
[self.lock lock];
// 获取该通知名称对应的所有观察者
NSMutableArray<HYBObserver *> *observers = [self.observerDict[notification.name] mutableCopy];
[self.lock unlock];
if (!observers.count) return;
// 遍历观察者,分发通知(注意:需过滤已释放的观察者)
for (HYBObserver *obs in observers) {
// 匹配发送者:如果观察者指定了 object,必须与通知的 object 一致
if (obs.object && obs.object != notification.object) continue;
// 观察者已释放,跳过
if (!obs.observer) {
[self _removeInvalidObserver:obs];
continue;
}
// 分发通知:SEL 方式
if (obs.selector) {
// 检查观察者是否实现了该方法
if ([obs.observer respondsToSelector:obs.selector]) {
// 用 objc_msgSend 发送消息(需关闭 ARC 或桥接)
((void (*)(id, SEL, HYBNotification *))objc_msgSend)(obs.observer, obs.selector, notification);
}
}
// 分发通知:Block 方式
else if (obs.block) {
dispatch_async(obs.queue, ^{
obs.block(notification);
});
}
}
}
- (void)postNotificationName:(NSString *)name
object:(id)object {
[self postNotificationName:name object:object userInfo:nil];
}
- (void)postNotificationName:(NSString *)name
object:(id)object
userInfo:(NSDictionary *)userInfo {
HYBNotification *notification = [HYBNotification notificationWithName:name object:object userInfo:userInfo];
[self postNotification:notification];
}
#pragma mark - 移除观察者
- (void)removeObserver:(id)observer {
[self removeObserver:observer name:nil object:nil];
}
- (void)removeObserver:(id)observer
name:(NSString *)name
object:(id)object {
[self.lock lock];
// 如果指定了名称,只移除该名称下的观察者
if (name) {
NSMutableArray *observers = self.observerDict[name];
[observers filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(HYBObserver *obs, NSDictionary *bindings) {
return !(obs.observer == observer && (object == nil || obs.object == object));
}]];
}
// 未指定名称,遍历所有通知名称移除
else {
[self.observerDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSMutableArray *observers, BOOL *stop) {
[observers filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(HYBObserver *obs, NSDictionary *bindings) {
return !(obs.observer == observer && (object == nil || obs.object == object));
}]];
}];
}
// 移除 Block 观察者的占位对象
[self.blockObservers removeObject:observer];
[self.lock unlock];
}
#pragma mark - 私有方法:移除无效观察者
- (void)_removeInvalidObserver:(HYBObserver *)observer {
[self.lock lock];
NSMutableArray *observers = self.observerDict[observer.name];
[observers removeObject:observer];
if (observers.count == 0) {
[self.observerDict removeObjectForKey:observer.name];
}
[self.lock unlock];
}
@end
4. 使用示例
// 1. 注册 SEL 类型观察者
[[HYBNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleNotification:)
name:@"HYBTestNotification"
object:nil];
// 2. 注册 Block 类型观察者
id blockObserver = [[HYBNotificationCenter defaultCenter] addObserverForName:@"HYBTestNotification"
object:nil
queue:dispatch_get_main_queue()
usingBlock:^(HYBNotification *notification) {
NSLog(@"Block 接收到通知:%@, userInfo: %@", notification.name, notification.userInfo);
}];
// 3. 发送通知
[[HYBNotificationCenter defaultCenter] postNotificationName:@"HYBTestNotification"
object:nil
userInfo:@{@"key": @"value"}];
// 4. 移除观察者
[[HYBNotificationCenter defaultCenter] removeObserver:self];
[[HYBNotificationCenter defaultCenter] removeObserver:blockObserver];
// 响应方法
- (void)handleNotification:(HYBNotification *)notification {
NSLog(@"SEL 接收到通知:%@, userInfo: %@", notification.name, notification.userInfo);
}
三、自定义通知中心的优化方向
-
通知合并:模仿
NSNotificationQueue,实现通知的缓冲和合并策略(如同一通知短时间内多次发送只执行一次)。 - 多播委托:结合委托模式,支持多观察者的委托回调。
- 内存优化:定期清理无效观察者(如通过 RunLoop 定时检查),避免字典中存储大量已释放的观察者。
-
跨进程通知:基于
CFNotificationCenter实现跨进程的通知分发(原生NSNotificationCenter仅支持进程内)。 - 类型安全:通过泛型或协议约束,让通知的 userInfo 更具类型安全性。
总结
OC 原生通知中心的核心是观察者模式 + 哈希表管理观察者 + 线程安全的通知分发;自定义通知中心时,需重点解决弱引用、线程安全、观察者匹配、通知分发四大问题。上述实现覆盖了原生通知中心的核心功能,同时保留了扩展性,可根据实际需求进一步优化。







网友评论