系统不是已经有通知中心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]; }
希望对大家有帮助。