内容简介: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 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Coming of Age in Second Life
Tom Boellstorff / Princeton University Press / 2008-04-21 / USD 29.95
The gap between the virtual and the physical, and its effect on the ideas of personhood and relationships, is the most interesting aspect of Boellstorff's analysis... Boellstorff's portrayal of a virt......一起来看看 《Coming of Age in Second Life》 这本书的介绍吧!