標籤:
系統不是已經有通知中樞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;@endPostNotification(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 - otherstatic 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];}
希望對大家有協助。
iOS NSNotificationCenter (自己實現一個通知中樞XMCNotificationCenter)