内容简介:NSNotificationCenter是一种通知调度机制,允许向注册的观察者广播信息。一个消息可以被多个观察者接收,一个观察者也可以接收多个消息。NSNotificationCenter只能在单个应用程序中传递通知;如果要将通知发布到其他进程或接收来自其他进程的通知,请使用NSDistributedNotificationCenter(注意只在macOS 10.0+可以使用,iOS中不可使用)。
NSNotificationCenter是一种通知调度机制,允许向注册的观察者广播信息。
一个消息可以被多个观察者接收,一个观察者也可以接收多个消息。
NSNotificationCenter只能在单个应用程序中传递通知;如果要将通知发布到其他进程或接收来自其他进程的通知,请使用NSDistributedNotificationCenter(注意只在macOS 10.0+可以使用,iOS中不可使用)。
NSNotificationCenter的使用详解
用前须知
通知三要素:通知名称(name,必填)、发送者(object,可选)和内容(userInfo,可选)。这个就像是发邮件一样,一定要指定邮件的主题(对应通知名称),否则是不让发送的。发送者和内容可以不指定。
API中也指明了name属性不能为nil。
API的使用
添加观察者,订阅通知
-
-(void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject; --(target方式)
当name为空时,表示任意名称的通知都订阅。
当object为空时,表示任意发送者都通知都订阅。
当name和object都为空时,表示订阅所有通知,含系统通知,可以通过此方法监听到所有系统通知。
当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的正确姿势:
- +(instancetype)notificationWithName:(NSNotificationName)aName object:(nullable id)anObject;--(注意:name为必填)
- +(instancetype)notificationWithName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;--(注意:name为必填)
- -(instancetype)initWithName:(NSNotificationName)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo; --(注意:name为必填)
-
-(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都不为空,表示指定名称和发送者的通知都取消订阅。
NSNotificationCenter的注意事项
使用target方式添加的observer是否需要移除?
官方文档说明:
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:方法移除。
使用block方式添加的observer的注意事项
block内不能强引用调用self,否则会产生内存泄漏
因为NSNotificationCenter持有block,而block持有observer(即当前ViewController),所以当前ViewController不能被释放。
需要手动移除,否则block一直在NSNotificationCenter中
官方文档说明:
The block is copied by the notification center and (the copy) held until the observer registration is removed.
也就是说,如果不手动移除的话,block就会一直存在。
验证方式:
操作步骤: 1、ViewControllerA push -> ViewControllerB 2、ViewControllerB pop -> ViewControllerA 3、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]; } 复制代码
阅读源码
gnustep源码
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 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
锋利的jQuery
单东林、张晓菲、魏然 / 人民邮电出版社 / 2009-6 / 39.00元
《锋利的jQuery》循序渐进地对jQuery的各种函数和方法调用进行了介绍,读者可以系统地掌握jQuery的DOM操作、事件监听和动画、表单操作、AJAX以及插件方面等知识点,并结合每个章节后面的案例演示进行练习,达到掌握核心知识点的目的。为使读者更好地进行开发实践,《锋利的jQuery》的最后一章将前7章讲解的知识点和效果进行了整合,打造出一个非常有个性的网站,并从案例研究、网站材料、网站结构......一起来看看 《锋利的jQuery》 这本书的介绍吧!