iOS NSNotificationCenter (自己实现一个通知中心XMCNotificationCenter)

   系统不是已经有通知中心NSNotificationCenter了吗?为什么还要自己实现一个呢?下面我们就考虑以下例子(下面大部分是我抄下我在github上写的说明及原码):

在iOS中模块间通知我们用得最多的就是NSNotificationCenter。举个例子,现在我们有一个模块需要抛一个通知出来,通知其它模块用户名改变了,我们来看代码大致是怎么写的

发通知一方
NSString *const kUserNameChangedNotification = @"UserNameChangedNotification";
NSString *const kUserOldNameKey = @"UserOldNameKey";
NSString *const kUserNewNameKey = @"UserNewNameKey";

NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter postNotificationName:UserNameChangedNotification object:nil userInfo:@{kUserOldNameKey:@"oldName",UserNewNameKey:"newName"}];

接收通知的一方可以是
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(UserNameChanged:) name:kUserNameChangedNotification object:nil];
也可以是
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(UserNameChangedNotification:) name:kUserNameChangedNotification object:nil];

从例子中可以看到有的缺点:
1.对于接收同一个事件的通知,不同的人可能会用不同的方法名,无法统一。
2.对于多参数支持不方便。

出于以上两点,写了这个XMCNotificationCenter,对应上面情况会变成以下

发通知一方
@protocol UserObserver <NSObject>
- (void)userNameChangedWithOldName:(NSString *)oldName newName:(NSString *)newName;
@end
PostNotification(UserObserver, @selector(userNameChangedWithOldName:newName:), userNameChangedWithOldName:@"oldName", newName:@"newName");

接收通知的一方是
AddObserverWithProtocol(self, UserObserver);并实现UserObserver协议的userNameChangedWithOldName:newName:方法即可释构时移除通知
RemoveObserver(self);

XMCNotificationCenter里面方法不多,使用简单,先定义协议如UserObserver,并添加里面需要实现的方法,方法为required或optional都可以
接下来就是通知者,只要使用PostNotification宏,参数分别是协议、方法的SEL、方法调用(这里还会有XCode的提示输入)。
最后就是观察者,使用AddObserverWithProtocol添加自己需要观察的协议,并实现协议相关方法,如果是optional的方法,不实现也就不会接收到通知。释构时使用RemoveObserver移除通知。

XMCNotificationCenter不能完全替代NSNotificationCenter。因为系统很多行为是靠NSNotificationCenter通知出来的。但如果通知是由我们自己发出,都可以使用XMCNotificationCenter。实现原理也很简单,利用了宏展开的特性,用宏使得发送通知像调用函数一样方便。具体代码如下,代码量不多:(也可以到我的github上的XMCNotificationCenter看)

//
//  XMCNotificationCenter.h
//  XMCNotificationCenter
//
//  Created by xianmingchen on 16/7/5.
//  Copyright © 2016年 xianmingchen. All rights reserved.
//

#import <Foundation/Foundation.h>

//添加或删除监听
#define AddObserverWithProtocol(observer, observerProtocol) [[XMCNotificationCenter defaultCenter] addObserver:observer withProtocolKey:@protocol(observerProtocol)]
#define RemoveObserverWithProtocol(observer, observerProtocol) [[XMCNotificationCenter defaultCenter] removeObserver:observer withProtocolKey:@protocol(observerProtocol)]
#define RemoveObserver(observer) [[XMCNotificationCenter defaultCenter] removeObserver:observer];

//抛通知
#define PostNotification(observerProtocol, selector, func) \
{     NSArray *__observers__ = [[XMCNotificationCenter defaultCenter] observersWithProtocolKey:@protocol(observerProtocol)];    for (id observer in __observers__)     {         if ([observer respondsToSelector:selector])         {             [observer func];         }     } }

typedef Protocol *ObserverProtocolKey;
@interface XMCNotificationCenter : NSObject

+ (XMCNotificationCenter *)defaultCenter;

- (void)addObserver:(id)observer withProtocolKey:(ObserverProtocolKey)key;
- (void)removeObserver:(id)observer withProtocolKey:(ObserverProtocolKey)key;
- (void)removeObserver:(id)observer;
- (NSArray *)observersWithProtocolKey:(ObserverProtocolKey)key;
@end
/
//  XMCNotificationCenter.mm
//  XMCNotificationCenter
//
//  Created by xianmingchen on 16/7/5.
//  Copyright © 2016年 xianmingchen. All rights reserved.
//

#import "XMCNotificationCenter.h"

static CFRange fullRangeWithArray(CFArrayRef array);
static void ObserversCallbackFunc(const void *_key, const void *_value, void *context);

struct ObserverContext {
    __weak XMCNotificationCenter *center;
    __unsafe_unretained id observer;
};

@interface XMCNotificationCenter ()
{
    CFMutableDictionaryRef observersDictionary;
    dispatch_semaphore_t semaphore;
}
@end

@implementation XMCNotificationCenter
+ (XMCNotificationCenter *)defaultCenter
{
    static XMCNotificationCenter *center = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
       center = [[XMCNotificationCenter alloc] init];
    });

    return center;
}

- (id)init
{
    self = [super init];
    if (self) {

        CFDictionaryValueCallBacks kCallBack;
        kCallBack.version = 0;
        observersDictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        semaphore = dispatch_semaphore_create(1);
    }
    return self;
}

#pragma mark - Add & Remove
- (void)addObserver:(id)observer withProtocolKey:(ObserverProtocolKey)key
{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    if (![observer conformsToProtocol:key])
    {
    #ifdef DEBUG
        NSParameterAssert(@"observer not conformsToProtocol");
    #endif
        NSLog(@"client doesnot conforms to protocol: %@", NSStringFromProtocol(key));
    }

    CFStringRef cfStringKey =  (__bridge CFStringRef)NSStringFromProtocol(key);
    CFMutableArrayRef observersArray = (CFMutableArrayRef)CFDictionaryGetValue(observersDictionary, cfStringKey);
    if (observersArray == NULL)
    {
        observersArray = CFArrayCreateMutable(NULL, 0, NULL);
        CFDictionaryAddValue(observersDictionary, cfStringKey, (const void *)observersArray);
    }

    CFRange range = fullRangeWithArray(observersArray);

    BOOL isContains = CFArrayContainsValue(observersArray, range, (__bridge const void *)(observer));
    if (!isContains)
    {
        CFArrayAppendValue(observersArray, (__bridge const void *)observer);
    }

    dispatch_semaphore_signal(semaphore);
}

- (void)removeObserver:(id)observer withProtocolKey:(ObserverProtocolKey)key
{
    CFStringRef cfStringKey =  (__bridge CFStringRef)NSStringFromProtocol(key);
    [self p_removeObserver:observer withKey:cfStringKey];
}

- (void)p_removeObserver:(id)observer withKey:(CFStringRef)key
{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    CFMutableArrayRef observersArray = (CFMutableArrayRef)CFDictionaryGetValue(observersDictionary, key);

    CFRange range = fullRangeWithArray(observersArray);

    CFIndex index = CFArrayGetFirstIndexOfValue(observersArray, range, (__bridge const void *)observer);
    if (index != -1)
    {
        CFArrayRemoveValueAtIndex(observersArray, index);
    }

    dispatch_semaphore_signal(semaphore);
}

- (void)removeObserver:(id)observer
{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    ObserverContext context;
    context.center = self;
    context.observer = observer;
    CFDictionaryApplyFunction(observersDictionary, &ObserversCallbackFunc, &context);
    dispatch_semaphore_signal(semaphore);
}

#pragma mark - get
- (NSArray *)observersWithProtocolKey:(ObserverProtocolKey)key
{
    CFStringRef cfStringKey =  (__bridge CFStringRef)NSStringFromProtocol(key);
    CFArrayRef cfArray = (CFArrayRef)CFDictionaryGetValue(observersDictionary, cfStringKey);
    NSArray *array = (__bridge NSArray *)cfArray;

    return array;
}

@end
#pragma mark - other
static CFRange fullRangeWithArray(CFArrayRef array)
{
    CFRange range;
    if (array == NULL)
    {
        return range;
    }

    CFIndex length = CFArrayGetCount(array) - 1;
    if (length < 0) {
        length = 0;
    }
    range.location = 0;

    range.length = length;
    return range;
}

static void ObserversCallbackFunc(const void *_key, const void *_value, void *context) {
    if (!context || !_value || !_key)
    {
        return;
    }

    XMCNotificationCenter *center = ((ObserverContext *)context)->center;
    id observer = ((ObserverContext *)context)->observer;

    [center p_removeObserver:observer withKey:(CFStringRef)_key];
}

希望对大家有帮助。

时间: 2024-08-07 04:32:45

iOS NSNotificationCenter (自己实现一个通知中心XMCNotificationCenter)的相关文章

iOS开发UI篇章之通知中心(NSNotificationCenter)

一.通知中心(NSNotificationCenter) 每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象 之间的消息通信 • 任何一个对象都可以向通知中心发布通知(NSNotification),描述自己在做什么.其他感 兴趣的对象(Observer)可以申请在某个特定通知发布时(或在某个特定的对象发布通知时) 二.通知(NSNotification) •一个完整的通知一般包含3个属性: Ø- (NSString *)name; // 通知的

iOS 通知中心 NSNotificationCenter

iOS开发中,每个app都有一个通知中心,通知中心可以发送和接收通知. 在使用通知中心 NSNotificationCenter之前,先了解一下通知 NSNotification. NSNotification 可以理解为消息对象,包含三个成员变量,如下: @property (readonly, copy) NSString *name; @property (nullable, readonly, retain) id object; @property (nullable, readonl

iOS之NSNotificationCenter通知中心使用事项

其实这里的通知和之前说到的KVO功能很想,也是用于监听操作的,但是和KVO不同的是,KVO只用来监听属性值的变化,这个发送监听的操作是系统控制的,我们控制不了,我们只能控制监听操作,类似于Android中系统发送的广播,我们只能接受.但是通知就不一样了,他的监听发送也是又我们自己控制,我们可以在任何地方任何时机发送一个通知,类似于Android中开发者自己发送的广播.从这一点看来,通知的使用场景更为广泛了. 下面就来看一下例子: 还是护士和小孩的那个例子 Children.h [objc] vi

IOS中通知中心NSNotificationCenter应用总结

一.了解几个相关的类 1.NSNotification 这个类可以理解为一个消息对象,其中有三个成员变量. 这个成员变量是这个消息对象的唯一标识,用于辨别消息对象. @property (readonly, copy) NSString *name; 这个成员变量定义一个对象,可以理解为针对某一个对象的消息. @property (readonly, retain) id object; 这个成员变量是一个字典,可以用其来进行传值. @property (readonly, copy) NSDi

IOS中通知中心NSNotificationCenter(自定义或系统自带)应用总结

一.了解几个相关的类 1.NSNotification 这个类可以理解为一个消息对象,其中有三个成员变量. 这个成员变量是这个消息对象的唯一标识,用于辨别消息对象. @property (readonly, copy) NSString *name; 这个成员变量定义一个对象,可以理解为针对某一个对象的消息. @property (readonly, retain) id object; 这个成员变量是一个字典,可以用其来进行传值. @property (readonly, copy) NSDi

iOS开发UI篇—通知中心(NSNotificationCenter)

一.通知中心(NSNotificationCenter) 每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象 之间的消息通信 •任何一个对象都可以向通知中心发布通知(NSNotification),描述自己在做什么.其他感兴趣的对象(Observer)可以申请在某个特定通知发布时(或在某个特定的对象发布通知时) 二.通知(NSNotification) • 一个完整的通知一般包含3个属性: - (NSString *)name;// 通知的名称

IOS中通知中心(NSNotificationCenter)的使用总结

一.了解几个相关的类 1.NSNotification 这个类可以理解为一个消息对象,其中有三个成员变量. 这个成员变量是这个消息对象的唯一标识,用于辨别消息对象. @property (readonly, copy) NSString *name; 这个成员变量定义一个对象,可以理解为针对某一个对象的消息. @property (readonly, retain) id object; 这个成员变量是一个字典,可以用其来进行传值. @property (readonly, copy) NSDi

iOS 设计模式-NSNotificationCenter 通知中心

通知介绍 每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信 任何一个对象都可以向通知中心发布通知(NSNotification),描述自己在做什么. 其他感兴趣的对象(Observer)可以申请在某个特定通知发布时(或在某个特定的对象发布通知时)收到这个通知 初始化通知中心 // 初始化通知中心 NSNotificationCenter *center =[NSNotificationCenter defaultCenter];

IOS开发 - 通知中心(NSNotificationCenter)

通知中心(NSNotificationCenter) 每一个应用程序都有一个通知中心(NSNotificationCenter)实例,专门负责协助不同对象之间的消息通信 任何一个对象都可以向通知中心发布通知(NSNotification),描述自己在做什么.其他感兴趣的对象(Observer)可以申请在某个特定通知发布时(或在某个特定的对象发布通知时)收到这个通知 通知(NSNotification) 一个完整的通知一般包含3个属性: - (NSString *)name; //通知的名称 -