系統不是已經有通知中心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]; }
希望對大家有幫助。