iOS Principle:Notification(下)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: iOS Principle:Notification(下)

消息合并处理

- (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];
}


image.png

设置成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(存放数据模型),里面存放观察者对象。如下图


image.png


实现探究

根据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"]);
}

运行结果


image.png


发送和处理在不同线程,实现通知的效果

以上原理解析文章来源:https://www.jianshu.com/p/051a9a3af1a4https://www.jianshu.com/p/087a35d5f778https://blog.csdn.net/qq_18505715/article/details/76146575


目录
相关文章
|
iOS开发
iOS Principle:CGAffineTransform
iOS Principle:CGAffineTransform
182 0
iOS Principle:CGAffineTransform
|
安全 Unix API
iOS Principle:CALayer(下)
iOS Principle:CALayer(下)
171 0
iOS Principle:CALayer(下)
|
iOS开发
iOS Principle:CALayer(中)
iOS Principle:CALayer(中)
149 0
iOS Principle:CALayer(中)
|
API C语言 iOS开发
iOS Principle:CALayer(上)
iOS Principle:CALayer(上)
179 0
iOS Principle:CALayer(上)
|
存储 缓存 iOS开发
iOS Principle:weak
iOS Principle:weak
168 0
iOS Principle:weak
|
设计模式 iOS开发
iOS Principle:Notification(上)
iOS Principle:Notification(上)
132 0
iOS Principle:Notification(上)
|
Web App开发 JSON 移动开发
iOS Principle:ReactNative(下)
iOS Principle:ReactNative(下)
183 0
iOS Principle:ReactNative(下)
|
移动开发 前端开发 JavaScript
iOS Principle:ReactNative(中)
iOS Principle:ReactNative(中)
126 0
iOS Principle:ReactNative(中)
|
传感器 调度 iOS开发
iOS Principle:Runloop(下)
iOS Principle:Runloop(下)
140 0
iOS Principle:Runloop(下)
|
iOS开发
iOS Principle:Runloop(中)
iOS Principle:Runloop(中)
102 0
iOS Principle:Runloop(中)