NSNotificationCenter是一种通知调度机制,容许向注册的观察者广播信息。php
一个消息能够被多个观察者接收,一个观察者也能够接收多个消息。git
NSNotificationCenter只能在单个应用程序中传递通知;若是要将通知发布到其余进程或接收来自其余进程的通知,请使用NSDistributedNotificationCenter(注意只在macOS 10.0+可使用,iOS中不可以使用)。github
通知三要素:通知名称(name,必填)、发送者(object,可选)和内容(userInfo,可选)。这个就像是发邮件同样,必定要指定邮件的主题(对应通知名称),不然是不让发送的。发送者和内容能够不指定。swift
API中也指明了name属性不能为nil。bash
-(void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject; --(target方式)数据结构
当name为空时,表示任意名称的通知都订阅。app
当object为空时,表示任意发送者都通知都订阅。less
当name和object都为空时,表示订阅全部通知,含系统通知,能够经过此方法监听到全部系统通知。post
当name和object都不为空时,表示只订阅指定通知名称和发送者都通知。测试
-(id )addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block --(block方式)
-(void)postNotification:(NSNotification *)notification;
建立NSNotification的正确姿式:
-(void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject; --(注意:name为必填)
-(void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;--(注意:name为必填)
-(void)removeObserver:(id)observer;
observer必填字段,表示取消当前观察者对象下的全部订阅。
-(void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject;
observer必填字段,表示当前观察者对象。
name为空,表示任何名字的通知都取消订阅。
object为空,表示任意发送者的通知都取消订阅。
name和object都为空,等同于-(void)removeObserver:(id)observer,表示取消当前观察者对象中的全部订阅。
name和object都不为空,表示指定名称和发送者的通知都取消订阅。
官方文档说明:
If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method. Otherwise, you should call removeObserver:name:object: before observer or any object passed to this method is deallocated.
就是说iOS 9.0之后,不须要在dealloc method中移除observer了,不然须要在dealloc中调用removeObserver:name:object:方法移除。
由于NSNotificationCenter持有block,而block持有observer(即当前ViewController),因此当前ViewController不能被释放。
官方文档说明:
The block is copied by the notification center and (the copy) held until the observer registration is removed.
也就是说,若是不手动移除的话,block就会一直存在。
验证方式:
操做步骤:
一、ViewControllerA push -> ViewControllerB
二、ViewControllerB pop -> ViewControllerA
三、ViewControllerA发送通知
ViewControllerB viewDidLoad方法中add observer:
self.testObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"TestName" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
NSLog(@"note = %@", note);
}];
ViewControllerB dealloc方法中加入Log,观察ViewControllerB是否正常释放:
NSLog(@"%s", __func__);
ViewControllerA 点击按钮发送通知:
[[NSNotificationCenter defaultCenter] postNotificationName:@"TestName" object:nil];
测试结果是:ViewControllerB释放后,还能打印note,表示block没有被释放。
复制代码
正确姿式:
__weak typeof(self) weakSelf = self;
self.testObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"TestName" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
__strong typeof(weakSelf) self = weakSelf;
if (!self) return;
//TODO 写具体逻辑
}];
-(void)dealloc {
//必定要释放
[[NSNotificationCenter defaultCenter] removeObserver:self.testObserver];
}
复制代码
www.gnustep.org/resources/d… 中下载GNUstep Base 1.26.0。 代码比较简单,配合如下核心数据结构图自行阅读便可。
nameless:哈希表,键为object,值为观察者集合(链表存储)。存储的是全部只有object的观察者信息。
name:哈希表,键为name,值为object的哈希表。存储name不能空的全部观察者信息。
wildcard:观察者集合。存储name和object都为空的观察者信息。
代码比较简单,自行阅读便可。
JBNotificationCenter.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NSString* JBNotificationName;
@interface JBNotification : NSObject
+ (instancetype)notificationWithName:(JBNotificationName)aName
object:(nullable id)anObject;
+ (instancetype)notificationWithName:(JBNotificationName)aName
object:(nullable id)anObject
userInfo:(nullable NSDictionary *)aUserInfo;
@end
@interface JBNotificationCenter : NSObject
@property (class, readonly, strong) JBNotificationCenter *defaultCenter;
/**
添加观察者
@param observer 观察者
@param aSelector 回调方法
@param aName 通知名称
@param anObject 通知关联对象
*/
- (void)addObserver:(id)observer
selector:(SEL)aSelector
name:(nullable JBNotificationName)aName
object:(nullable id)anObject;
/**
发送通知
@param notification 通知对象
*/
- (void)postNotification:(JBNotification *)notification;
/**
发送通知
@param aName 通知名称
@param anObject 通知关联对象
*/
- (void)postNotificationName:(JBNotificationName)aName
object:(nullable id)anObject;
/**
发送通知
@param aName 通知名称
@param anObject 通知关联对象
@param aUserInfo 参数
*/
- (void)postNotificationName:(JBNotificationName)aName
object:(nullable id)anObject
userInfo:(nullable NSDictionary *)aUserInfo;
/**
删除观察者
@param observer 观察者
*/
- (void)removeObserver:(id)observer;
/**
删除观察者
@param observer 观察者
@param aName 通知名称
@param anObject 通知关联对象
*/
- (void)removeObserver:(id)observer
name:(nullable JBNotificationName)aName
object:(nullable id)anObject;
/**
添加观察者
@param name 通知名称
@param obj 通知关联对象
@param queue 通知所属队列
@param block 回调
@return 担任操做观察者的隐藏对象
*/
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(JBNotification *note))block;
@end
NS_ASSUME_NONNULL_END
复制代码
JBNotificationCenter.m
#import "JBNotificationCenter.h"
@interface JBNotificationReceiver : NSObject
@property(nonatomic, weak) id observer; //观察者
@property(nonatomic, assign) SEL selector; //观察者回调选择器
@property(nonatomic, copy) NSString *name;//通知名称
@property(nonatomic, weak) id sender;//发送者
@end
@implementation JBNotificationReceiver
@end
@interface JBNotification ()
@property(nonatomic, copy) NSString *name;
@property(nonatomic, strong) id object;
@property(nonatomic, strong) NSDictionary *userInfo;
@end
@implementation JBNotification
+ (instancetype)notificationWithName:(JBNotificationName)aName
object:(nullable id)anObject {
return [self notificationWithName:aName object:anObject userInfo:nil];
}
+ (instancetype)notificationWithName:(JBNotificationName)aName
object:(nullable id)anObject
userInfo:(nullable NSDictionary *)aUserInfo {
JBNotification *notification = [[JBNotification alloc] init];
notification.name = aName;
notification.object = anObject;
notification.userInfo = aUserInfo;
return notification;
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@ %p {name = %@; object = %@}",self.class,self, self.name, self.object];
}
@end
typedef void (^JBNotificationBlock)(JBNotification *note);
@interface JBBlockNotificationObserver : NSObject
@property(nonatomic, weak) NSOperationQueue *queue;
@property(nonatomic, copy) JBNotificationBlock block;
- (id) initWithQueue: (NSOperationQueue *)queue block: (JBNotificationBlock)block;
@end
@implementation JBBlockNotificationObserver
- (id) initWithQueue: (NSOperationQueue *)queue block: (JBNotificationBlock)block {
self = [super init];
if (self) {
self.queue = queue;
self.block = block;
}
return self;
}
- (void) didReceiveNotification: (JBNotification *)note {
if (self.queue && self.queue != NSOperationQueue.currentQueue) {
[self.queue addOperationWithBlock:^{
self.block(note);
}];
[self.queue waitUntilAllOperationsAreFinished];
} else {
self.block(note);
}
}
@end
@interface JBNotificationCenter ()
@property(nonatomic, strong) NSMutableArray<JBNotificationReceiver *> *observers;
@property(nonatomic, strong) NSLock *observerLock;
@end
@implementation JBNotificationCenter
+ (instancetype)defaultCenter {
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
self.observers = [[NSMutableArray alloc] initWithCapacity:0];
self.observerLock = [[NSLock alloc] init];
}
return self;
}
/**
添加观察者
@param observer 观察者
@param aSelector 回调方法
@param name 通知名称
@param object 通知关联对象
*/
- (void)addObserver:(id)observer
selector:(SEL)aSelector
name:(nullable JBNotificationName)name
object:(nullable id)object {
//参数的校验
if (observer == nil)
[NSException raise: NSInvalidArgumentException
format: @"Nil observer passed to addObserver ..."];
if (aSelector == 0)
[NSException raise: NSInvalidArgumentException
format: @"Null selector passed to addObserver ..."];
if ([observer respondsToSelector: aSelector] == NO){
[NSException raise: NSInvalidArgumentException
format: @"[%@-%@] Observer '%@' does not respond to selector '%@'",
NSStringFromClass([self class]), NSStringFromSelector(_cmd),
observer, NSStringFromSelector(aSelector)];
}
[self.observerLock lock];
JBNotificationReceiver *notificationReceiver = [[JBNotificationReceiver alloc] init];
notificationReceiver.observer = observer;
notificationReceiver.selector = aSelector;
notificationReceiver.name = name;
notificationReceiver.sender = object;
[self.observers addObject:notificationReceiver];
[self.observerLock unlock];
}
/**
添加观察者
@param name 通知名称
@param object 通知关联对象
@param queue 通知所属队列
@param block 回调
@return 担任操做观察者的隐藏对象
*/
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)object queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(JBNotification *note))block {
[self.observerLock lock];
JBNotificationReceiver *notificationReceiver = [[JBNotificationReceiver alloc] init];
JBBlockNotificationObserver *observer = [[JBBlockNotificationObserver alloc] initWithQueue:queue block:block];
notificationReceiver.observer = observer;
notificationReceiver.selector = @selector(didReceiveNotification:);
notificationReceiver.name = name;
notificationReceiver.sender = object;
[self.observers addObject:notificationReceiver];
[self.observerLock unlock];
return observer;
}
/**
发送通知
@param name 通知名称
@param object 通知关联对象
*/
- (void)postNotificationName:(JBNotificationName)name object:(nullable id)object {
[self postNotificationName:name object:object userInfo:nil];
}
/**
发送通知
@param name 通知名称
@param object 通知关联对象
@param userInfo 参数
*/
- (void)postNotificationName:(JBNotificationName)name
object:(nullable id)object
userInfo:(nullable NSDictionary *)userInfo {
JBNotification *notification = [JBNotification notificationWithName:name object:object userInfo:userInfo];
[self postNotification:notification];
}
/**
发送通知
@param notification 通知对象
*/
- (void)postNotification:(JBNotification *)notification {
//查找符合条件的observer
[self.observerLock lock];
NSMutableArray<JBNotificationReceiver *> *sendTo = [[NSMutableArray alloc] init];
for (JBNotificationReceiver *receiver in self.observers) {
if ((receiver.name == nil || [receiver.name isEqualToString:notification.name])
&&(receiver.sender == nil || [receiver.sender isEqual:notification.object])) {
[sendTo addObject:receiver];
}
}
[self.observerLock unlock];
NSMutableArray<JBNotificationReceiver *> *delete = [[NSMutableArray alloc] init];
for (JBNotificationReceiver *receiver in sendTo) {
if (receiver.observer) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[receiver.observer performSelector:receiver.selector withObject:notification];
#pragma clang diagnostic pop
} else {
[delete addObject:receiver];
}
}
[self.observerLock lock];
[self.observers removeObjectsInArray:delete];
[self.observerLock unlock];
}
/**
删除观察者
@param observer 观察者
*/
- (void)removeObserver:(id)observer {
[self removeObserver:observer name:nil object:nil];
}
/**
删除观察者
@param observer 观察者
@param name 通知名称
@param object 通知关联对象
*/
- (void)removeObserver:(id)observer
name:(nullable JBNotificationName)name
object:(nullable id)object {
[self.observerLock lock];
NSMutableArray<JBNotificationReceiver *> *keep = [[NSMutableArray alloc] init];
for (JBNotificationReceiver *receiver in self.observers) {
//通知对象不同,留下
if (receiver.observer != nil && receiver.observer != observer) {
[keep addObject:receiver];
continue;
}
//通知对象同样的状况下,当前名称不为空,且名称不同,留下
if (name != nil && ![name isEqualToString:receiver.name]) {
[keep addObject:receiver];
continue;
}
//通知名称同样的状况下,当前sender不为空,且发送者不同,留下
if (object != nil && receiver.observer != object) {
[keep addObject:receiver];
}
}
self.observers = keep;
[self.observerLock unlock];
}
@end
复制代码