美文网首页
Notification

Notification

作者: 陈_振 | 来源:发表于2018-08-14 12:48 被阅读0次

    Notification的命名方式及定义方法

    [Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification

    Apple范例:

    NSApplicationDidBecomeActiveNotification
    NSWindowDidMiniaturizeNotification
    NSTextViewDidChangeSelectionNotification
    NSColorPanelColorDidChangeNotification

    在发送通知的实现文件中,按如下方式定义:

    NSNotificationName const 通知名 = @"text notification";
    

    在需要接收改通知的类文件的顶部按如下方式声明该通知变量:

    UIKIT_EXTERN NSNotificationName const 通知名;
    

    NSNotificationName==NSString *
    UIKIT_EXTERN==extern

    具体详情可以参考下面大神的链接。

    参考来源:http://www.jianshu.com/p/761f302c0bd5

    NSNotification用法

    总结自南峰子的技术博客
    总结自天口三水羊的简书

    添加观察者的两种方式:

    方式一:

    - (void)addObserver:(id)notificationObserver
               selector:(SEL)notificationSelector
                   name:(NSString *)notificationName
                 object:(id)notificationSender
    
    1. notificationObserver不能为nil。
    2. notificationSelector回调方法有且只有一个参数(NSNotification对象)。
    3. 如果notificationName为nil,则会接收所有的通知(如果notificationSender不为空,则接收所有来自于notificationSender的所有通知)。
    4. 如果notificationSender为nil,则会接收所有notificationName定义的通知;否则,接收由notificationSender发送的通知。
    5. 监听同一条通知的多个观察者,在通知到达时,它们执行回调的顺序是不确定的,所以我们不能去假设操作的执行会按照添加观察者的顺序来执行。

    方式二:

    - (id<NSObject>)addObserverForName:(NSString *)name
                                object:(id)obj
                                 queue:(NSOperationQueue *)queue
                            usingBlock:(void (^)(NSNotification *note))block
    
    1. name和obj为nil时的情形与前面一个方法是相同的。

    2. 如果queue为nil,则消息是默认在post线程中同步处理,即通知的post与转发是在同一线程中;但如果我们指定了操作队列,不管通知是在哪个线程中post的,都会在Operation Queue所属的线程中进行转发。

    3. block块会被通知中心拷贝一份(执行copy操作),以在堆中维护一个block对象,直到观察者被从通知中心中移除。所以,应该特别注意在block中使用外部对象,避免出现对象的循环引用。

    4. 如果一个给定的通知触发了多个观察者的block操作,则这些操作会在各自的Operation Queue中被并发执行。所以我们不能去假设操作的执行会按照添加观察者的顺序来执行。
      该方法会返回一个表示观察者的对象,记得在不用时释放这个对象。

    5. 关于注册监听者,还有一个需要注意的问题是,每次调用addObserver时,都会在通知中心重新注册一次,即使是同一对象监听同一个消息,而不是去覆盖原来的监听。这样,当通知中心转发某一消息时,如果同一对象多次注册了这个通知的观察者,则会收到多个通知。

    移除观察者的方式

    - (void)removeObserver:(id)notificationObserver
    - (void)removeObserver:(id)notificationObserver
                                        name:(NSString *)notificationName
                                       object:(id)notificationSender
    
    1. 由于注册观察者时(不管是哪个方法),通知中心会维护一个观察者的弱引用,所以在释放对象时,要确保移除对象所有监听的通知。否则,可能会导致程序崩溃或一些莫名其妙的问题。

    2. 对于第二个方法,如果notificationName为nil,则会移除所有匹配notificationObserver和notificationSender的通知,同理notificationSender也是一样的。而如果notificationName和notificationSender都为nil,则其效果就与第一个方法是一样的了。

    3. 最有趣的应该是这两个方法的使用时机。–removeObserver:适合于在类的dealloc方法中调用,这样可以确保将对象从通知中心中清除;而在viewWillDisappear:这样的方法中,则适合于使用-removeObserver:name:object:方法,以避免不知情的情况下移除了不应该移除的通知观察者。例如,假设我们的ViewController继承自一个类库的某个ViewController类(假设为SKViewController吧),可能SKViewController自身也监听了某些通知以执行特定的操作,但我们使用时并不知道。如果直接在viewWillDisappear:中调用–removeObserver:,则也会把父类监听的通知也给移除。

    post消息

    - postNotification:
    – postNotificationName:object:
    – postNotificationName:object:userInfo:
    
    
    1. 每次post一个通知时,通知中心都会去遍历一下它的分发表,然后将通知转发给相应的观察者。
    2. 通知的发送与处理是同步的,在某个地方post一个消息时,会等到所有观察者对象执行完处理操作后,才回到post的地方,继续执行后面的代码。

    通知中心是如何维护观察者对象的

    上面这个问题在《斯坦福大学公开课:iOS 7应用开发》的第5集的第57分50秒中得到了解答:确实使用的是unsafe_unretained,老师的解释是,之所以使用unsafe_unretained,而不使用weak,是为了兼容老版本的系统。

    iOS8及以前,NSNotificationCenter持有的是观察者的unsafe_unretained指针(可能是为了兼容老版本),这样,在观察者回收的时候未removeOberser,而后再进行post操作,则会向一段被回收的区域发送消息,所以出现野指针crash。而iOS9以后,unsafe_unretained改成了weak指针,即使dealloc的时候未removeOberser,再进行post操作,则会向nil发送消息,所以没有任何问题。
    

    Notification Queues和异步通知

    异步通知原理
    创建一个NSNotificationQueue队列(first in-first out),将定义的NSNotification放入其中,并为其指定三种状态之一:

    typedef NS_ENUM(NSUInteger, NSPostingStyle) {
        NSPostWhenIdle = 1,      // 当runloop处于空闲状态时post
        NSPostASAP = 2,    // 当当前runloop完成之后立即post
        NSPostNow = 3    // 立即post,同步(为什么需要这种type,且看三.3)
    };
    

    异步通知的使用

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        A *a = [A new];
        [a test];
        self.a = a;
        NSNotification *noti = [NSNotification notificationWithName:@"111" object:nil];
        [[NSNotificationQueue defaultQueue] enqueueNotification:noti postingStyle:NSPostASAP];
        //[[NSNotificationQueue defaultQueue] enqueueNotification:noti postingStyle:NSPostWhenIdle];
        //[[NSNotificationQueue defaultQueue] enqueueNotification:noti postingStyle:NSPostNow];
        NSLog(@"测试同步还是异步");
        return YES;
    }
    
    // 输出
    2017-02-26 19:56:32.805 notification[19406:12719309] 测试同步还是异步
    2017-02-26 19:56:32.816 notification[19406:12719309] selector 1
    2017-02-26 19:56:32.816 notification[19406:12719309] block 2
    

    Notification Queues的合成作用

    NSNotificationQueue除了有异步通知的能力之外,也能对当前队列的通知根据NSNotificationCoalescing类型进行合成(即将几个合成一个)。

    typedef NS_OPTIONS(NSUInteger, NSNotificationCoalescing) {
        NSNotificationNoCoalescing = 0,  // 不合成
        NSNotificationCoalescingOnName = 1,  // 根据NSNotification的name字段进行合成
        NSNotificationCoalescingOnSender = 2  // 根据NSNotification的object字段进行合成
    };
    

    指定Thread处理通知

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        A *a = [A new];
        [a test];
        self.a = a;
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
        dispatch_async(queue, ^{
            NSLog(@"current thread %@", [NSThread currentThread]);
            [[NSNotificationCenter defaultCenter] postNotificationName:@"111" object:nil];
        });
        return YES;
    }
    

    可知,a中的observer的selector将会在DISPATCH_QUEUE_PRIORITY_BACKGROUND中执行,若该selector执行的是刷新UI的操作,那么这种方式显然是错误的。这里,我们需要保证selector永远在mainThread执行。所以,有以下方式,指定observer的回调方法的执行线程:

    // 代码
    @interface A : NSObject 
    - (void)test;
    @end
    @implementation A
    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    - (void)test {
        [[NSNotificationCenter defaultCenter] addObserverForName:@"111" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
            NSLog(@"current thread %@ 刷新UI", [NSThread currentThread]);
            // 刷新UI ...
        }];
    }
    @end
     
    // 输出
    current thread <NSThread: 0x7bf29110>{number = 3, name = (null)}
    2017-02-27 11:53:46.531 notification[29510:12833116] current thread <NSThread: 0x7be1d6f0>{number = 1, name = main} 刷新UI
    

    对象之间的通信方式主要有以下几种:

    1. 直接方法调用
    2. Target-Action
    3. Delegate
    4. 回调(block)
    5. KVO
    6. 通知

    相关文章

      网友评论

          本文标题:Notification

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