NSNotificationCenter

2025-12-23  本文已影响0人  白公子是猫奴

在 Objective-C 中,NSNotificationCenter(通知中心) 是基于观察者模式实现的跨对象通信机制,用于解耦不同组件间的消息传递。要理解它的内部工作原理,以及自己如何实现一个简易版通知中心,我们可以从底层原理自定义实现两方面展开分析。

一、OC 原生通知中心(NSNotificationCenter)的内部工作原理

NSNotificationCenter 的核心是管理观察者的注册、通知的发送与分发,其内部主要分为主线程通知中心全局通知中心+defaultCenter),核心逻辑包括以下几个关键环节:

1. 观察者注册(addObserver 相关方法)

当调用 addObserver:selector:name:object:addObserverForName:object:queue:usingBlock: 时,通知中心会做这些事:

2. 发送通知(postNotification 相关方法)

当调用 postNotification:postNotificationName:object:userInfo: 时,通知中心会执行:

3. 移除观察者(removeObserver 相关方法)

当调用 removeObserver:removeObserver:name:object: 时,通知中心会:

4. 特殊的通知队列(NSNotificationQueue)

NSNotificationQueue 是通知的缓冲队列,基于 RunLoop 实现,用于批量处理通知:

5. 线程特性

二、自定义实现一个简易的 OC 通知中心

如果要自己实现一个通知中心,核心需要实现观察者注册、通知发送、观察者移除三大功能,同时要解决弱引用、线程安全、匹配规则等问题。以下是分步实现思路和代码示例:

设计思路

  1. 数据结构:用字典存储观察者信息,键为通知名称+发送者的组合,值为观察者数组。
  2. 观察者模型:封装观察者的对象、响应方法/Block、队列等信息,避免直接存储强引用。
  3. 线程安全:使用锁(NSLock/dispatch_semaphore_t)保证多线程下的读写安全。
  4. 弱引用处理:对观察者对象使用弱引用,避免循环引用。
  5. 通知分发:支持同步分发,可选异步分发(基于 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);
}

三、自定义通知中心的优化方向

  1. 通知合并:模仿 NSNotificationQueue,实现通知的缓冲和合并策略(如同一通知短时间内多次发送只执行一次)。
  2. 多播委托:结合委托模式,支持多观察者的委托回调。
  3. 内存优化:定期清理无效观察者(如通过 RunLoop 定时检查),避免字典中存储大量已释放的观察者。
  4. 跨进程通知:基于 CFNotificationCenter 实现跨进程的通知分发(原生 NSNotificationCenter 仅支持进程内)。
  5. 类型安全:通过泛型或协议约束,让通知的 userInfo 更具类型安全性。

总结

OC 原生通知中心的核心是观察者模式 + 哈希表管理观察者 + 线程安全的通知分发;自定义通知中心时,需重点解决弱引用、线程安全、观察者匹配、通知分发四大问题。上述实现覆盖了原生通知中心的核心功能,同时保留了扩展性,可根据实际需求进一步优化。

上一篇 下一篇

猜你喜欢

热点阅读