美文网首页
NSNotification

NSNotification

作者: NapoleonY | 来源:发表于2020-08-07 20:41 被阅读0次

Objective-C 代码

[[NSNotificationCenter defaultCenter] postNotificationName:TEST_NOTIFICATION object:nil userInfo:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(processNotification:) name:TEST_NOTIFICATION object:nil];

- (void)processNotification:(NSNotification *)notification {
}

Swift 代码

NotificationCenter.default.post(name: NSNotification.Name(rawValue: NOTI_WXLOGIN_AUTHORIZED), object: authResp.code)

NotificationCenter.default.addObserver(self, selector: #selector(handleWeChatLoginSuccess(_:)), name: NSNotification.Name(rawValue: NOTI_WXLOGIN_AUTHORIZED), object: nil)

@objc private func handleWeChatLoginSuccess(_ noti:NSNotification) {
}

线程安全

NSNotification 是线程安全的,但也不是绝对安全。例如:

  1. 当我们注册一个观察者时,通知中心会持有观察者的一个弱引用,来确保观察者是可用的。
  2. 调用dealloc操作会让 Observer 对象的引用计数减为 0,这时对象会被释放掉。
  3. 其他线程发送一个通知,如果此时 Observer 还未被释放,则会向其转发消息,并执行回调方法。而如果在回调执行的过程中对象被释放了,就会出现 crash。

几点说明

  1. NSNotification 消息的同步性

    • NSNotification 使用的是同步操作,即如果程序中 A 位置 post 了一个 NSNotification,在 B、C 位置注册了 Observer,通知发出后,必须等到 B、C 位置的通知回调执行完毕才能返回到 A 处继续往下执行
    • 如果想让 NSNotification 的 post 处 和 observer 处异步执行,可以通过 NSNotificationQueue 实现
  2. 多个观察者的执行顺序

    对于同一个通知,如果注册了多个观察者,则多个观察者的执行顺序和它们的注册顺序是保持一致的

  3. NSNotification 通知转发线程

    • NSNotificationCenter 在转发 NSNotification 消息时,在哪个线程 post,就在哪个线程转发。即,不管 observer 是在哪个线程,observer 的回调方法执行线程都和 post 的线程保持一致
    • 如果想让 post 的线程和转发的线程不同,可以通过 NSNotification 重定向技术实现。
    // NSNotification 重定向技术
    /*
    思路:
         1. 自定义个通知队列(注意不是 NSNotificationQueue 对象而是一个数组),让这个队列去维护那些需要重定向的 Notification,我们仍然像平常一样去注册一个通知的观察者
         2. 当 Notification 来了时,先看 post 这个 Notification 的线程是不是我们所期望的线程,如果不是,则将这个 Notification 存储到我们的队列中,并发送一个信号(signal)到期望的线程中,来告诉这个线程需要处理一个 Notification。
         3. 指定的线程收到信号后,将 Notification 从队列中移除,并进行处理
    */
    
    @property (nonatomic) NSMutableArray    *notifications;         // 通知队列
    @property (nonatomic) NSThread          *notificationThread;    // 期望线程
    @property (nonatomic) NSLock            *notificationLock;      // 用于对通知队列加锁的锁对象,避免线程冲突
    @property (nonatomic) NSMachPort        *notificationPort;      // 用于向期望线程发送信号的通信端口
    
    - (void)postNotify {
        NSLog(@"current thread = %@", [NSThread currentThread]);
    
        // 初始化
        self.notifications = [[NSMutableArray alloc] init];
        self.notificationLock = [[NSLock alloc] init];
        
        self.notificationThread = [NSThread currentThread];
        self.notificationPort = [[NSMachPort alloc] init];
        self.notificationPort.delegate = self;
    
        // 往当前线程的run loop添加端口源
        // 当Mach消息到达而接收线程的run loop没有运行时,则内核会保存这条消息,直到下一次进入run loop
        [[NSRunLoop currentRunLoop] addPort:self.notificationPort
                                    forMode:(__bridge NSString *)kCFRunLoopCommonModes];
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(processNotification:) name:TEST_NOTIFICATION object:nil];
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:TEST_NOTIFICATION object:nil userInfo:nil];
        
        });
    }
    
    - (void)processNotification:(NSNotification *)notification {
        if ([NSThread currentThread] != _notificationThread) {
            // Forward the notification to the correct thread.
            [self.notificationLock lock];
            [self.notifications addObject:notification];
            [self.notificationLock unlock];
            [self.notificationPort sendBeforeDate:[NSDate date]
                                       components:nil
                                             from:nil
                                         reserved:0];
            NSLog(@"11111current thread = %@", [NSThread currentThread]);
        }
        else {
            // Process the notification here;
            NSLog(@"current thread = %@", [NSThread currentThread]);
            NSLog(@"process notification");
        }
    }
    
    - (void)handleMachMessage:(void *)msg {
        NSLog(@"33333current thread = %@", [NSThread currentThread]);
        [self.notificationLock lock];
        
        while ([self.notifications count]) {
            NSNotification *notification = [self.notifications objectAtIndex:0];
            [self.notifications removeObjectAtIndex:0];
            [self.notificationLock unlock];
            [self processNotification:notification];
            [self.notificationLock lock];
        };
        
        [self.notificationLock unlock];
    }
    
  1. addObserver 和 removeObserver 必须成对出现

注意

  1. 尽量在一个线程中处理通知相关的操作,大部分情况下,这样做都能确保通知的正常工作。不过,我们无法确定到底会在哪个线程中调用 dealloc 方法,所以这一点还是比较困难。
  2. 注册监听都时,使用基于block的API。这样我们在block还要继续调用self的属性或方法,就可以通过weak-strong的方式来处理。具体大家可以改造下上面的代码试试是什么效果。
  3. 使用带有安全生命周期的对象,这一点对象单例对象来说再合适不过了,在应用的整个生命周期都不会被释放。
  4. 使用代理。

参考

  1. Notification与多线程
  2. NSNotification的几点说明
  3. 深入理解RunLoop

相关文章

  • NSNotification

    发通知 NSNotification *deleteMyCommemtN =[NSNotification not...

  • 通知使用

    创建对象 NSNotification *notification =[NSNotification notifi...

  • iOS通知中心

    有关的类 NSNotification NSNotificationCenter NSNotification N...

  • 通知中心(NSNotificationCenter)总结

    一、简介 1. NSNotification 理解 NSNotification@property (readon...

  • 推送通知-本地推送

    iOS推送通知 注意:这里说的推送通知跟NSNotification有所区别 NSNotification是抽象的...

  • NSNotification是同步还是异步?和delegate相

    1、NSNotification是同步还是异步?默认情况下,创建的NSNotification是同步的,发布通知 ...

  • iOS 推送后台语音播报

    推送通知 注意:这里说的推送通知跟NSNotification有所区别 NSNotification是抽象的,不可...

  • iOS本地推送

    1. 推送通知简介 1.1: 这里说的推送通知跟NSNotification有所区别 NSNotification...

  • NSNotification

    当你定义你自己的 NSNotification的时候你应该把你的通知的名字定义为一个字符串常量,就像你暴露给其他类...

  • NSNotification

    通常我们在 iOS 中发生什么事件时该做什么是由 Delegate 实现的,例如 View 加载完后会触发 vie...

网友评论

      本文标题:NSNotification

      本文链接:https://www.haomeiwen.com/subject/htpqdktx.html