消息合并处理
- (void)sendAsyncNotificationTwo { NSNotificationQueue * notificationQueue = [NSNotificationQueue defaultQueue]; NSNotification * notification = [NSNotification notificationWithName:@"EdisonNotif" object:nil]; NSNotification * notificationtwo = [NSNotification notificationWithName:@"EdisonNotif" object:nil]; NSLog(@"异步发送通知before:%@",[NSThread currentThread]); [notificationQueue enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil]; [notificationQueue enqueueNotification:notificationtwo postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil]; [notificationQueue enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil]; NSLog(@"异步发送通知after:%@",[NSThread currentThread]); NSPort * port = [NSPort new]; [[NSRunLoop currentRunLoop] addPort:port forMode:NSRunLoopCommonModes]; [[NSRunLoop currentRunLoop] run]; }
设置成NSNotificationCoalescingOnName按名称合并,此时我连续发送三条,但是只处理了一次, 再继续,上面代码就只是把发送时机改成NSPostNow
[notificationQueue enqueueNotification:notification postingStyle:NSPostNow coalesceMask:NSNotificationCoalescingOnName forModes:nil]; [notificationQueue enqueueNotification:notificationTwo postingStyle:NSPostNow coalesceMask:NSNotificationCoalescingOnName forModes:nil]; [notificationQueue enqueueNotification:notification postingStyle:NSPostNow coalesceMask:NSNotificationCoalescingOnName forModes:nil];
打印结果
/* 2017-08-30 16:30:04.114 通知的底层解析[4442:164620] 异步发送通知before:<NSThread: 0x600000269a40>{number = 3, name = (null)} 2017-08-30 16:30:04.115 通知的底层解析[4442:164620] 接收到通知了:<NSThread: 0x600000269a40>{number = 3, name = (null)} 2017-08-30 16:30:04.115 通知的底层解析[4442:164620] 接收到通知了:<NSThread: 0x600000269a40>{number = 3, name = (null)} 2017-08-30 16:30:04.115 通知的底层解析[4442:164620] 接收到通知了:<NSThread: 0x600000269a40>{number = 3, name = (null)} 2017-08-30 16:30:04.116 通知的底层解析[4442:164620] 异步发送通知after:<NSThread: 0x600000269a40>{number = 3, name = (null)} */
结果就打印了处理了三次通知,这个应该好理解吧,就跟dispatch_sync原理一样,就是得发送因为NSPostNow是同步的,所以发送第一条通知,得等处理完第一条通知,才跑发送第二条通知,这样肯定就没有合并消息一说了,因为这有点类似线程阻塞的意思,只有异步,就是三个发送通知全部跑完,在处理通知的时候看是否需要合并和怎么合并,再去处理
系统的很多方法,如 drawRect,就是默认消息合并处理,多次方法只响应一次
实现原理
先猜想一下
首先,信息的传递就依靠通知(NSNotification),也就是说,通知就是信息(执行的方法,观察者本身(self),参数)的包装。
通知中心(NSNotificationCenter)是个单例,向通知中心注册观察者,也就是说,这个通知中心有个集合,这个集合存放着观察者。
可以想象的是,发送通知需要name参数,添加观察者也有个name参数,这两个name一样的时候,当发送通知时候,观察者对象就能接受到信息,执行对应的操作。那么这个集合很容易想到就是NSDictionary!
key就是name,value就是NSArray(存放数据模型),里面存放观察者对象。如下图
实现探究
根据NSNotification&NSNotificationCenter接口给出实现代码,创建两个新类YFLNotification,YFLNotificationCenter,这两个类的接口和苹果提供的接口完全一样,我将根据接口提供的功能给出实现代码。
要点是通知中心是单例类,并且通知中心维护了一个包含所有注册的观察者的集合,这里我选择了动态数组来存储所有的观察者,源码如下:
+ (YFLNotificationCenter*)defaultCenter { static YFLNotificationCenter *singleton; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ singleton = [[self alloc] initSingleton]; }); return singleton; } - (instancetype)initSingleton { if ([super init]) { _obsetvers = [[NSMutableDictionary alloc] init]; } return self; }
还定义了一个观察者模型用于保存观察者,通知消息名,观察者收到通知后执行代码所在的操作队列和执行代码的回调,模型源码如下:
@interface YFLObserverModel: 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
向通知中心注册观察者,源码如下:
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSString*)aName object:(nullable id)anObject { //如果不存在,那么即创建 if (![self.obsetvers objectForKey:aName]) { NSMutableArray *arrays = [[NSMutableArray alloc]init]; // 创建数组模型 YFLObserverModel *observerModel = [[YFLObserverModel alloc]init]; observerModel.observer = observer; observerModel.selector = aSelector; observerModel.notificationName = aName; observerModel.object = anObject; [arrays addObject:observerModel]; //填充进入数组 [self.obsetvers setObject:arrays forKey:aName]; } else { //如果存在,取出来,继续添加减去即可 NSMutableArray *arrays = (NSMutableArray*)[self.obsetvers objectForKey:aName]; // 创建数组模型 YFLObserverModel *observerModel = [[YFLObserverModel alloc]init]; observerModel.observer = observer; observerModel.selector = aSelector; observerModel.notificationName = aName; observerModel.object = anObject; [arrays addObject:observerModel]; } } - (id <NSObject>)addObserverForName:(nullable NSString *)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(YFLNotification *note))block { //如果不存在,那么即创建 if (![self.obsetvers objectForKey:name]) { NSMutableArray *arrays = [[NSMutableArray alloc]init]; // 创建数组模型 YFLObserverModel *observerModel = [[YFLObserverModel alloc]init]; observerModel.block = block; observerModel.notificationName = name; observerModel.object = obj; observerModel.operationQueue = queue; [arrays addObject:observerModel]; //填充进入数组 [self.obsetvers setObject:arrays forKey:name]; } else { //如果存在,取出来,继续添加即可 NSMutableArray *arrays = (NSMutableArray*)[self.obsetvers objectForKey:name]; // 创建数组模型 YFLObserverModel *observerModel = [[YFLObserverModel alloc]init]; observerModel.block = block; observerModel.notificationName = name; observerModel.object = obj; observerModel.operationQueue = queue; [arrays addObject:observerModel]; } return nil; }
发送通知有三种方式,最终都是调用- (void)postNotification:(YFLNotification *)notification,源码如下:
- (void)postNotification:(YFLNotification *)notification { //name 取出来对应观察者数组,执行任务 NSMutableArray *arrays = (NSMutableArray*)[self.obsetvers objectForKey:notification.name]; [arrays enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { //取出数据模型 YFLObserverModel *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]; } }]; }
底层通信 port
通知队列也可以实现异步,但是真正的异步还是得通过port
底层所有的消息触发都是通过端口 NSPort 来进行操作的
NSPort 接口通信实现代码
@interface ViewController ()<NSPortDelegate> { NSPort *_port; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [self testPortDemo]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // NSPort // [self sendPort]; [NSThread detachNewThreadSelector:@selector(sendPort) toTarget:self withObject:nil]; } - (void)testPortDemo { _port =[[NSPort alloc] init]; //消息处理通过代理来处理的 _port.delegate = self; //把端口加在哪个线程里,就在哪个线程进行处理,下面:加在当前线程的runloop里 [[NSRunLoop currentRunLoop] addPort:_port forMode:NSRunLoopCommonModes]; } //发送消息 - (void)sendPort { NSLog(@"port发送通知before:%@",[NSThread currentThread]); [_port sendBeforeDate:[NSDate date] msgid:1212 components:nil from:nil reserved:0]; NSLog(@"port发送通知after:%@",[NSThread currentThread]); } //处理消息 - (void)handlePortMessage:(NSPortMessage *)message { NSLog(@"port处理任务:%@",[NSThread currentThread]); NSObject * messageObj = (NSObject*)message; NSLog(@"=%@",[messageObj valueForKey:@"msgid"]); }
运行结果
发送和处理在不同线程,实现通知的效果
以上原理解析文章来源:https://www.jianshu.com/p/051a9a3af1a4,https://www.jianshu.com/p/087a35d5f778,https://blog.csdn.net/qq_18505715/article/details/76146575