通知的构成:
在 iOS 中,NSNotification
和NSNotificationCenter
是使用观察者模式
来实现的用于跨层传递消息。
观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并自动更新。
首先,信息的传递就依靠通知(NSNotification)
,也就是说,通知就是信息(执行的方法,观察者本身(self),参数)
的包装。
那么这个集合很容易想到就是NSDictionary!
,key
就是name
,value
就是NSArray(存放数据模型)
,里面存放观察者
对象。当发送通知时,在通知的字典,根据``name找到
value,这个
value就是一
数组,数组里面存放
数据模型(observer、SEL)`。即可执行对应的行为
观察者对象的模型
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
添加通知的实现
1、先通过通知名作为key去Map中取key对应的存放注册模型的数组
2、找不到key的话,重新添加一个键值对
3、找到key对应的动态数组,把观察者对象添加到动态数组中
发送通知的实现方式
//发送通知时,在Map中通过key找到数组,然后判断是selector还是block调用,然后调用
- (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)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, @"当前通知中心没有观察者");
}
}
}
在
MRC
时代,通知中心持有的是注册者的unsafe_unretained
指针,在注册者被回收时若不对通知进行手动移除,则指针指向被回收的内存区域,成为野指针。这时再发送通知,便会造成crash
。而在iOS 9
以后,通知中心持有的是注册者的weak
指针,这时即使不对通知进行手动移除,指针也会在注册者被回收后自动置空。
- (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));
//这个API来注册通知,可以直接传入block类型参数。使用这个API会导致注册者被系统retain,因此仍然需要像以前一样手动移除通知,同时这个block类型参数也需注意避免循环引用。
网友评论