一、基本概念

NSNotificationNSNotificationCenter是使用观察者模式来实现的用于跨层传递消息。

NSNotificationCenter采用单例模式。

二、基本实现

通知实现由三个类组成:NSNotificationCenter、NSNotification、NSObserverModel,对应关系和接口设计:

NSNotificationCenter是注册中心,发通知时,发送的是一个NSNotification对象。

NSNotification里面包含发通知需要传递的内容:

name(通知的字符串),

object(发送通知的对象),

userInfo(需要传递的参数),

NSObserverModel是注册通知时需要保持的参数,包括:

observer(观察者对象),

selector(执行的方法),

notificationName(通知名字),

object(携带参数),

operationQueue(队列),

block(回调),

三:原理图:

四:注意点:

1、每次调用addObserver时,都会在通知中心重新注册一次,即使是同一对象,监听同一个消息,而不是去覆盖原来的监听。这样,当通知中心转发某一消息时,如果同一对象多次注册了这个通知的观察者,则会收到多个通知。
2、observer 观察者(不能为nil,通知中心会弱引用,ARC是iOS9之前是unsafe_unretained,iOS9及以后是weak,MRC是assign,所以这也是MRC不移除会crash,ARC不移除不会crash的原因)

五:仿照写一个通知中心:WYNotificationCenter,WYNotification,WYObserverModel

#import <Foundation/Foundation.h>

@class WYNotification;
NS_ASSUME_NONNULL_BEGIN typedef void(^OperationBlock)(WYNotification *); @interface WYObserverModel : NSObject
@property (nonatomic, weak) 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
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface WYNotification : NSObject

@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; @end NS_ASSUME_NONNULL_END
#import "WYNotification.h"

@implementation WYNotification

- (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;
} @end
#import "WYNotificationCenter.h"
#import "WYObserverModel.h"
#import "WYNotification.h" @interface WYNotificationCenter()
@property(nonatomic,strong)NSMutableDictionary *obsetvers;
@end @implementation WYNotificationCenter + (WYNotificationCenter *)defaultCenter {
static WYNotificationCenter *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
instance.obsetvers = [NSMutableDictionary dictionary];
});
return instance;
} - (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSString*)aName object:(nullable id)anObject
{ // 创建数据模型
WYObserverModel *observerModel = [[WYObserverModel alloc] init];
observerModel.observer = observer;
observerModel.selector = aSelector;
observerModel.notificationName = aName;
observerModel.object = anObject; // 如果不存在,才创建
if (![self.obsetvers objectForKey:aName]) {
NSMutableArray *arrays = [NSMutableArray array]; [arrays addObject:observerModel]; // 添加进 json 中
[self.obsetvers setObject:arrays forKey:aName];
} else {
// 如果存在,取出来,继续添加进对应数组即可
NSMutableArray *arrays = (NSMutableArray *)[self.obsetvers objectForKey:aName]; [arrays addObject:observerModel];
} } - (id <NSObject>)addObserverForName:(nullable NSString *)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(WYNotification *note))block
{
WYObserverModel *observerModel = [[WYObserverModel alloc] init];
observerModel.block = block;
observerModel.notificationName = name;
observerModel.object = obj;
observerModel.operationQueue = queue; //如果不存在,那么即创建
if (![self.obsetvers objectForKey:name]) { NSMutableArray *arrays = [[NSMutableArray alloc]init];
[arrays addObject:observerModel];
//填充进入数组
[self.obsetvers setObject:arrays forKey:name]; }else{
//如果存在,取出来,继续添加即可
NSMutableArray *arrays = (NSMutableArray*)[self.obsetvers objectForKey:name];
[arrays addObject:observerModel];
}
return nil;
}
- (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 {
WYNotification *notification = [[WYNotification alloc] initWithName:name object:object userInfo:userInfo];
[self postNotification:notification];
}
- (void)postNotification:(WYNotification *)notification
{
//name 取出来对应观察者数组,执行任务
NSMutableArray *arrays = (NSMutableArray*)[self.obsetvers objectForKey:notification.name];
NSLog(@"%@",arrays);
[arrays enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { //取出数据模型
WYObserverModel *observerModel = obj;
id observer = observerModel.observer;
SEL secector = observerModel.selector; if (!observerModel.operationQueue) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[observer performSelector:secector withObject:notification];
#pragma clang diagnostic pop
}else{ //创建任务
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ //这里用block回调出去
observerModel.block(notification); }]; // 如果添加观察者 传入 队列,那么就任务放在队列中执行(子线程异步执行)
NSOperationQueue *operationQueue = observerModel.operationQueue;
[operationQueue addOperation:operation]; } }]; } #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.obsetvers objectForKey:name]) {
NSMutableArray *arrays = (NSMutableArray *)[self.obsetvers objectForKey:name];
[arrays removeObject:observer];
} else {
// 移除观察者 - 当没有 name 参数时
if (self.obsetvers.allKeys.count > 0 && self.obsetvers.allValues.count > 0) {
NSArray *allKeys = self.obsetvers.allKeys; for (int i = 0; i < allKeys.count; i++) {
NSMutableArray *keyOfAllObservers = [self.obsetvers objectForKey:allKeys[i]]; BOOL isStop = NO; // 如果找到后就不再遍历后面的数据了 for (int j = 0; j < keyOfAllObservers.count; j++) {
// 取出数据模型
WYObserverModel *observerModel = keyOfAllObservers[j]; if (observerModel.observer == observer) {
[keyOfAllObservers removeObject:observerModel];
isStop = YES;
break;
}
} if (isStop) { // 找到了,退出循环
break;
}
}
} else {
NSAssert(false, @"当前通知中心没有观察者");
}
}
}
@end

六、参考:

https://www.jianshu.com/p/2c9fef32a383

https://blog.csdn.net/m0_37182854/article/details/78782724

iOS-NSNotificationCenter通知原理解析的更多相关文章

  1. IOS NSNotificationCenter 通知的使用

    1.注册通知 [NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notify) name:@" ...

  2. iOS 10 UserNotification框架解析 – 本地通知

    iOS 10以前的通知比较杂乱,把本地通知和远程通知分开了,诞生了许多功能类似的API,很容易让初学者犯迷糊.而iOS 10的通知把API做了统一,利用独立的UserNotifications.fra ...

  3. IOS中通知中心(NSNotificationCenter)

    摘要 NSNotification是IOS中一个调度消息通知的类,采用单例模式设计,在程序中实现传值.回调等地方应用很广.   IOS中通知中心NSNotificationCenter应用总结 一.了 ...

  4. APPcrawler基础原理解析及使用

    一.背景 一年前,我们一直在用monkey进行Android 的稳定性测试 ,主要目的就是为了测试app 是否会产生Crash,是否会有ANR,页面错误等问题,在monkey测试过程中,实现了脱离Ca ...

  5. [转载]iOS 10 UserNotifications 框架解析

    活久见的重构 - iOS 10 UserNotifications 框架解析 TL;DR iOS 10 中以前杂乱的和通知相关的 API 都被统一了,现在开发者可以使用独立的 UserNotifica ...

  6. 开源磁力搜索爬虫dhtspider原理解析

    开源地址:https://github.com/callmelanmao/dhtspider. 开源的dht爬虫已经有很多了,有php版本的,python版本的和nodejs版本.经过一些测试,发现还 ...

  7. iOS 10 UserNotifications 框架解析

    摘自:https://onevcat.com/2016/08/notification/ iOS 10 中以前杂乱的和通知相关的 API 都被统一了,现在开发者可以使用独立的 UserNotifica ...

  8. iOS - Notification 通知

    1.Notification 通知中心实际上是在程序内部提供了消息广播的一种机制,它允许我们在低程度耦合的情况下,满足控制器与一个任意的对象进行通信的目的.每一个 iOS 程序(即每一个进程)都有一个 ...

  9. IOS的XML文件解析,利用了NSData和NSFileHandle

    如果需要了解关于文档对象模型和XML的介绍,参看 http://www.cnblogs.com/xinchrome/p/4890723.html 读取XML 上代码: NSFileHandle *fi ...

随机推荐

  1. SaaS加速器II 能力中心:互利互补 共享商业红利

    摘要: 通过丰富的阿里集团和三方的业务能力API,缩短业务从0-1构建的周期和降低成本,我们希望能够把阿里巴巴在电商.金融.物流.高德以及其他领域沉淀出来商业最佳实践.商业能力,通过阿里云的渠道输出, ...

  2. AtCoder Beginner Contest 075 D - Axis-Parallel Rectangle【暴力】

    AtCoder Beginner Contest 075 D - Axis-Parallel Rectangle 我要崩溃,当时还以为是需要什么离散化的,原来是暴力,特么五层循环....我自己写怎么都 ...

  3. D - Denouncing Mafia DFS

    这道题其实很简单,求k个到根的链,使得链上的节点的个数尽可能多,如果节点被计算过了,就不能再被计算了,其实我们发现,只要k>=叶子节点,那么肯定是全部,所以我们考虑所有的叶子节点,DFS到根节点 ...

  4. mybatis-generator1.3.6的使用

    下载地址: http://blog.mybatis.org/2017/12/mybatis-generator-version-136-released.html 参考了 http://blog.cs ...

  5. offsetheight 和clientheight、scrollheight、scrollTop区别

    clientHeight:元素客户区的大小,指的是元素内容及其边框所占据的空间大小(经过实践取出来的大多是视口大小) scrollHeight: 滚动大小,指的是包含滚动内容的元素大小(元素内容的总高 ...

  6. 阿里云POLARDB荣膺2019中国数据库年度最佳创新产品

    在日前的DTCC 2019(第十届中国数据库技术大会)上,阿里云自研云原生数据库POLARDB获选2019中国数据库——“年度最佳创新产品”. POLARDB是阿里云在2018年正式商业化的云原生数据 ...

  7. 注意 Laravel 清除缓存 php artisan cache:clear 的一个坑

    Laravel 的命令 php artisan cache:clear 用来清除各种缓存,如页面,Redis,配置文件等缓存,它会清空 Redis 数据库的全部数据,比如默认使用的 Redis 的 数 ...

  8. 【BestCoder Round #93 1004】MG loves set

    [题目链接]:http://acm.hdu.edu.cn/showproblem.php?pid=6022 [题意] 让你求一个集合的子集数目; 这个子集有要求; 即: 它所有元素的平方的和小于它所有 ...

  9. rowStyle设置Bootstrap Table行样式

    日常开发中我们通常会用到隔行变色来美化表格,也会根据每行的数据显示特定的背景颜色,如果库存低于100的行显示红色背景 CSS样式 <style> .bg-blue { background ...

  10. [C#] ServiceStack.Redis如何批量的pop数据?

    要安全的批量pop数据,有两个办法: 1.用事务(不用事务的话可能导致重复读.ServiceStack的pipeline是没有自带事务的.) 2.执行lua脚本 我这里提供用事务的实现方法: publ ...