目录
- 一 通知的基本使用
- 基本概念
- 什么情况下使用通知
- 应用场景
- 如何使用通知
- 使用通知需要注意的细节
- 二 通知实现的原理
- 概述
- 三 编码实现
一 通知的基本使用
1.1 基本概念
NSNotification
是 iOS 中一个调度消息通知的类,采用单例模式
,在程序中实现传值,回调等地方,应用很广。在 iOS 中,NSNotification
和NSNotificationCenter
是使用观察者模式
来实现的用于跨层传递消息。
NSNotification声明
@interface NSNotification : NSObject <NSCopying, NSCoding>
@property (readonly, copy) NSNotificationName name;
@property (nullable, readonly, retain) id object;
@property (nullable, readonly, copy) NSDictionary *userInfo;
- (instancetype)initWithName:(NSNotificationName)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
@end
1.2 什么情况下使用通知
观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并自动更新。
1.3 应用场景
- 对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变
- 一个对象必须通知其他对象,而它又不知道其他对象是什么
1.4 如何使用通知
- 向观察者中心添加观察者(2种方式)
// 观察者接收到通知后执行任务的代码在发送通知的线程中执行
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
// 观察者接收到通知后执行任务的代码在指定的操作队列中执行
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
- 通知中心向观察者发送消息
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;
- 观察者接收到消息执行相应的行为
即执行上面向观察者中心添加观察者中 SEL 方法
- 在通知中心移除观察者
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject;
- 使用通知时需要注意哪些细节
1.通知一定要移除,在 dealloc 方法中移除,但是 iOS9 之后不再需要手动移除了,详情看 iOS 9 以后通知不再需要手动移除。
2.通知有分同步通知和异步通知,只是我们同步通知用的比较多。
3.不能用- (instancetype)init
初始化一个通知。
二 通知的实现原理
概述 : 首先,信息的传递就依靠通知(NSNotification)
,也就是说,通知就是信息(执行的方法,观察者本身(self),参数)
的包装。
通知中心(NSNotificationCenter)
是个单例
,向通知中心注册观察者,也就是说,这个通知中心有个集合
,这个集合存放着观察者。
那么这个集合是什么样的数据类型 ? 可以这么思考: 发送通知需要name
参数,添加观察者也有个name
参数,这两个name一样的时候,当发送通知时候,观察者对象就能接受到信息,执行对应的操作。
那么这个集合很容易想到就是NSDictionary!
,key
就是name
,value
就是NSArray(存放数据模型)
,里面存放观察者
对象。
当发送通知时,在通知的字典,根据``name
找到
value,这个
value就是一
数组,数组里面存放
数据模型(observer、SEL)`。即可执行对应的行为。
三 编码实现
根据NSNotification&NSNotificationCenter
接口给出实现代码,创建两个新类CSNotification
,CSNotificationCenter
,这两个类的接口和苹果提供的接口完全一样。
要点是通知中心是单例类
,并且通知中心维护了一个包含所有注册的观察者的集合,这里选择动态数组
来存储所有的观察者
。
3.1 CSNotification 类
- CSNotification.h
/**
关于通知实例对象的封装
*/
@interface CSNotification : NSObject
/** name */
@property(nonatomic, copy)NSString *name;
/** object */
@property(nullable, readonly, retain)id object;
/** userInfo */
@property(nullable, readonly, copy)NSDictionary *userInfo;
/**
初始化方法
@param name name
@param object object
@param userInfo userInfo
@return 通知实例对象
*/
- (instancetype)initWithName:(NSString *)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo;
+ (instancetype)initWithName:(NSString *)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo;
@end
- CSNotification.m
@implementation CSNotification
- (instancetype)init {
NSAssert(false, @"do not invoke; not a valid initializer for this class");
return nil;
}
- (instancetype)initWithName:(NSString *)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo {
self = [super init];
if (self) {
_name = name;
_object = object;
_userInfo = userInfo;
}
return self;
}
+ (instancetype)initWithName:(NSString *)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo {
return [[self alloc] initWithName:name object:object userInfo:userInfo];
}
注意不能使用
init
初始化,因为缺少必要的参数
3.2 CSNotificationCenter 类
- CSNotificationCenter.h
@interface CSNotificationCenter : NSObject
/// 单例
+ (CSNotificationCenter *)defaultCenter;
#pragma mark - 添加通知
- (void)addObserver:(nonnull id)observer selector:(nonnull SEL)selector name:(nullable NSString *)name object:(nullable id)object;
- (id <NSObject>)addObserverForName:(nullable NSString *)name object:(nullable id)object queue:(nullable NSOperationQueue *)queue usingBlock:(void(^)(CSNotification * _Nonnull note))block;
#pragma mark - 接收通知
- (void)postNotification:(CSNotification *)notification;
- (void)postNotificationName:(nonnull NSString *)name object:(nullable id)object;
- (void)postNotificationName:(nonnull NSString *)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo;
#pragma mark - 移除通知
- (void)removeObserver:(nonnull id)observer;
- (void)removeObserver:(nonnull id)observer name:(nullable NSString *)name object:(nullable id)object;
@end
- CSNotificationCenter.m
单例的实现
/// 单例
+ (CSNotificationCenter *)defaultCenter {
static CSNotificationCenter *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
instance.observerJson = [NSMutableDictionary dictionary];
});
return instance;
}
定义一个观察者模型用于保存观察者,通知消息名,观察者收到通知后执行代码所在的操作队列和执行代码的回调,模型源码如下:
typedef void(^operationBlock)(CSNotification *notification);
/**
观察者模型对象
*/
@interface CSObserverModel : NSObject
/** 观察者对象 */
@property(nonatomic, strong)id observer;
/** 执行的方法 */
@property(nonatomic, assign)SEL selector;
/** 通知名字 */
@property(nonatomic, copy)NSString *notificationName;
/** 携带的参数 */
@property(nonatomic, strong)id object;
/** 队列 */
@property(nonatomic, strong)NSOperationQueue *operationQueue;
/** 回调 */
@property(nonatomic, copy)operationBlock block;
@end
声明一个可变字典,用于存储通知模型数据
@interface CSNotificationCenter()
/** observerJson key:NotificationName value:observes */
@property(nonatomic, strong)NSMutableDictionary *observerJson;
@end
向通知中心注册观察者
#pragma mark - 添加通知
- (void)addObserver:(nonnull id)observer selector:(nonnull SEL)selector name:(nullable NSString *)name object:(nullable id)object {
// 创建数据模型
CSObserverModel *observerModel = [[CSObserverModel alloc] init];
observerModel.observer = observer;
observerModel.selector = selector;
observerModel.notificationName = name;
observerModel.object = object;
// 如果不存在,才创建
if (![self.observerJson objectForKey:name]) {
NSMutableArray *arrays = [NSMutableArray array];
[arrays addObject:observerModel];
// 添加进 json 中
[self.observerJson setObject:arrays forKey:name];
} else {
// 如果存在,取出来,继续添加进对应数组即可
NSMutableArray *arrays = (NSMutableArray *)[self.observerJson objectForKey:name];
[arrays addObject:observerModel];
}
}
- (id <NSObject>)addObserverForName:(nullable NSString *)name object:(nullable id)object queue:(nullable NSOperationQueue *)queue usingBlock:(void(^)(CSNotification * _Nonnull note))block {
// 创建数据模型
CSObserverModel *observerModel = [[CSObserverModel alloc] init];
observerModel.block = block;
observerModel.operationQueue = queue;
observerModel.notificationName = name;
observerModel.object = object;
// 如果不存在,才创建
if (![self.observerJson objectForKey:name]) {
NSMutableArray *arrays = [NSMutableArray array];
[arrays addObject:observerModel];
// 添加进 json 中
[self.observerJson setObject:arrays forKey:name];
} else {
// 如果存在,取出来,继续添加进对应数组即可
NSMutableArray *arrays = (NSMutableArray *)[self.observerJson objectForKey:name];
[arrays addObject:observerModel];
}
return nil;
}
发送通知有三种方式
#pragma mark - 发送通知
- (void)postNotification:(CSNotification *)notification {
// 根据 name 取出对应观察者数组,执行任务
NSMutableArray *arrays = (NSMutableArray *)[self.observerJson objectForKey:notification.name];
[arrays enumerateObjectsUsingBlock:^( CSObserverModel *observerModel, NSUInteger idx, BOOL *stop) {
// 取出数据模型
id observer = observerModel.observer;
SEL selector = observerModel.selector;
if (!observerModel.operationQueue) {
// 下面这样写的目的是:手动忽略clang编译器警告
// 参考:http://blog.csdn.net/qq_18505715/article/details/76087558
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[observer performSelector:selector withObject:notification];
#pragma clang diagnostic pop
} else {
// 创建任务
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
// 这里通过 block 回调出去
observerModel.block(notification);
}];
// 如果添加观察者 传入 队列,那么任务就放在队列中执行(子线程异步执行)
[observerModel.operationQueue addOperation:operation];
}
}];
}
- (void)postNotificationName:(nonnull NSString *)name object:(nullable id)objec {
[self postNotificationName:name object:objec userInfo:nil];
};
- (void)postNotificationName:(nonnull NSString *)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo {
CSNotification *notification = [[CSNotification alloc] initWithName:name object:object userInfo:userInfo];
[self postNotification:notification];
}
移除观察者
#pragma mark - 移除通知
- (void)removeObserver:(nonnull id)observer {
[self removeObserver:observer name:nil object:nil];
}
- (void)removeObserver:(nonnull id)observer name:(nullable NSString *)name object:(nullable id)object {
// 移除观察者 - 当有 name 参数时
if (name.length > 0 && [self.observerJson objectForKey:name]) {
NSMutableArray *arrays = (NSMutableArray *)[self.observerJson objectForKey:name];
[arrays removeObject:observer];
} else {
// 移除观察者 - 当没有 name 参数时
if (self.observerJson.allKeys.count > 0 && self.observerJson.allValues.count > 0) {
NSArray *allKeys = self.observerJson.allKeys;
for (int i = 0; i < allKeys.count; i++) {
NSMutableArray *keyOfAllObservers = [self.observerJson objectForKey:allKeys[i]];
BOOL isStop = NO; // 如果找到后就不再遍历后面的数据了
for (int j = 0; j < keyOfAllObservers.count; j++) {
// 取出数据模型
CSObserverModel *observerModel = keyOfAllObservers[j];
if (observerModel.observer == observer) {
[keyOfAllObservers removeObject:observerModel];
isStop = YES;
break;
}
}
if (isStop) { // 找到了,退出循环
break;
}
}
} else {
NSAssert(false, @"当前通知中心没有观察者");
}
}
}
3.3 测试用例
- 接受通知
- (void)addObserver {
// 1.使用系统方法添加观察者 - 观察者接收到通知后执行任务的代码在发送通知的线程中执行
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(change:) name:kTextFieldValueChange object:nil];
// 2.使用系统方法添加观察者 - 队列回调 - 观察者接收到通知后执行任务的代码在指定的操作队列中执行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[[NSNotificationCenter defaultCenter] addObserverForName:kTextFieldValueChange object:nil queue:queue usingBlock:^(NSNotification *note) {
// 异步回调主线程,与主线程通信
dispatch_async(dispatch_get_main_queue(), ^{
[self change:note];
});
}];
// 3.添加自定义观察者
[[CSNotificationCenter defaultCenter] addObserver:self selector:@selector(customChange:) name:kTextFieldValueChange object:nil];
// 4.添加自定义观察者 - 队列回调
[[CSNotificationCenter defaultCenter] addObserverForName:kTextFieldValueChange object:nil queue:queue usingBlock:^(CSNotification *note) {
// 异步回调主线程,与主线程通信
dispatch_async(dispatch_get_main_queue(), ^{
[self customChange:note];
});
}];
}
- (void)change:(NSNotification *)notification {
NSString *text = (NSString *)notification.object;
[self.lbe setText:text];
NSLog(@"当前线程是否是主线程: %d",[NSThread isMainThread]);
}
- (void)customChange:(CSNotification *)notification {
NSString *text = (NSString *)notification.object;
[self.lbe setText:text];
NSLog(@"当前线程是否是主线程: %d",[NSThread isMainThread]);
}
#pragma mark - dealloc
- (void)dealloc {
// remove observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:kTextFieldValueChange object:nil];
[[CSNotificationCenter defaultCenter] removeObserver:self name:kTextFieldValueChange object:nil];
}
- 发送通知
- (void)tapSend {
// 发送通知 - 系统
[[NSNotificationCenter defaultCenter] postNotificationName:kTextFieldValueChange object:self.textF.text];
// 发送通知 - 自定义
[[CSNotificationCenter defaultCenter] postNotificationName:kTextFieldValueChange object:self.textF.text];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.navigationController popViewControllerAnimated:YES];
});
}
- 运行结果如下 - 后期补上
通知接受数据正常
相关文章参考
iOS 9 以后通知不再需要手动移除
iOS Notification实现原理
网友评论