> NSNotification顾名思义就是通知的作用,一个对象通知另外一个对象,可以用来传递参数、通信等作用,与delegate的一对一不同,通知是一对多的。在一个对象中注册了通知,那么其他任意对象都可以来对这个对象发出通知。 这篇文章主要讲诉两个对象 - NSNotificationCenter - NSNotificationQueue ------------- 1、NSNotificationCenter ``` /** * 注册一个通知 * */ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getNotification:) name:@"notificationMethon" object:nil]; ``` NSNotificationCenter是一个单列,我们可以通过defaultCenter来获取到通知中心这个单列,使用通知的第一步就是添加通知。 ``` /** * 移除所有通知 */ [[NSNotificationCenter defaultCenter] removeObserver:self]; /** * 移除单个通知 * */ [[NSNotificationCenter defaultCenter] removeObserver:self name:@"notificationMethon" object:nil]; ``` 需要注意的是当我们添加一个通知以后,必须在合适的位置将通知移除,不然下次再添加这个通知并调用时,这个通知将会被调用多次,而这一般不是我们所预料的。一般我们在对象的析构函数中将通知移除,我们可以选择将这个对象中的所有通知移除,也可以选择一个一个按照通知的name来移除。 ``` /** * 发出通知 */ [[NSNotificationCenter defaultCenter] postNotificationName:@"notificationMethon" object:nil userInfo:@{@"key":@"value"}]; ``` 当我们添加通知以后,我们就可以发出通知,也是通过NSNotificationCenter类的defaultCenter来获取到通知中心,然后通过postNotificationName来发出通知。 这里需要注意的一点就是这样发出的通知是同步操作,也就是只有当发出的通知执行完毕以后才会继续执行接下去的代码。 ``` -(void)getNotification:(NSNotification *)info{ NSDictionary *dict = info.userInfo; } ``` 这是发出通知以后会调用的方法,在多线程操作时,发出通知的对象和接收通知的对象处于同一个线程。 在这个响应的方法中有一个参数`(NSNotification *)info`这个参数是NSNotification类型的,这个类型有几个属性: ``` @property (readonly, copy) NSString *name; @property (nullable, readonly, retain) id object; @property (nullable, readonly, copy) NSDictionary *userInfo; ``` 其中 userInfo 这个属性是一个字典,也是最为关键以及有用的属性,我们通过这个属性来传递参数等一些有用的信息。 总结以上内容,所以通知的最简单的使用过程就是: 1、添加通知; 2、发出通知; 3、移除通知; ------------------------------------ 2、NSNotificationQueue 上面说到 NSNotificationCenter是一个同步操作,也就是只有当响应的通知的代码执行完毕以后,发出通知的对象的代码才会继续往下执行。那么 NSNotificationQueue就有一些区别,他有两个非常重要的特点:即通告的聚结和异步发送。聚结是把和刚进入队列的通告相类似的其它通告从队列中移除的过程。如果一个新的通告和已经在队列中的通告相类似,则新的通告不进入队列,而所有类似的通告(除了队列中的第一个通告以外)都被移除。然而,您不应该依赖于这个特殊的聚结行为。 而异步发送则很好理解了,也就是说发出通知以后立刻返回,也就是是继续执行下面的代码,并不管通知发出后的具体情况 ``` /** 发出通知 */ NSNotification *notifacation = [[NSNotification alloc]initWithName:@"notificationMethon" object:nil userInfo:@{@"key":@"value1"}]; [[NSNotificationQueue defaultQueue] enqueueNotification:notifacation postingStyle:NSPostWhenIdle]; ``` 我们可以通过,NSNotificationQueue的defaultQueue来获取到这个通知队列,然后调用enqueueNotification来发出通知,我们可以看到第二个参数postingStyle,这个参数是一个枚举,他可以是以下三个值: ``` typedef NS_ENUM(NSUInteger, NSPostingStyle) { NSPostWhenIdle = 1, NSPostASAP = 2, NSPostNow = 3 }; ``` 这三个不同的值是有一定区别的。(以下内容摘抄自网络) 尽快发送 以NSPostASAP风格进入队列的通告会在运行循环的当前迭代完成时被发送给通告中心,如果当前运行循环模式和请求的模式相匹配的话(如果请求的模式和当前模式不同,则通告在进入请求的模式时被发出)。由于运行循环在每个迭代过程中可能进行多个调用分支(callout),所以在当前调用分支退出及控制权返回运行循环时,通告可能被分发,也可能不被分发。其它的调用分支可能先发生,比如定时器或由其它源触发了事件,或者其它异步的通告被分发了。 您通常可以将NSPostASAP风格用于开销昂贵的资源,比如显示服务器。如果在运行循环的一个调用分支过程中有很多客户代码在窗口缓冲区中进行描画,在每次描画之后将缓冲区的内容刷新到显示服务器的开销是很昂贵的。在这种情况下,每个draw...方法都会将诸如“FlushTheServer” 这样的通告排入队列,并指定按名称和对象进行聚结,以及使用NSPostASAP风格。结果,在运行循环的最后,那些通告中只有一个被派发,而窗口缓冲区也只被刷新一次。 空闲时发送 以NSPostWhenIdle风格进入队列的通告只在运行循环处于等待状态时才被发出。在这种状态下,运行循环的输入通道中没有任何事件,包括定时器和异步事件。以NSPostWhenIdle风格进入队列的一个典型的例子是当用户键入文本、而程序的其它地方需要显示文本字节长度的时候。在用户输入每一个字符后都对文本输入框的尺寸进行更新的开销是很大的(而且不是特别有用),特别是当用户快速输入的时候。在这种情况下,Cocoa会在每个字符键入之后,将诸如“ChangeTheDisplayedSize”这样的通告进行排队,同时把聚结开关打开,并使用NSPostWhenIdle风格。当用户停止输入的时候,队列中只有一个“ChangeTheDisplayedSize”通告(由于聚结的原因)会在运行循环进入等待状态时被发出,显示部分也因此被刷新。请注意,运行循环即将退出(当所有的输入通道都过时的时候,会发生这种情况)时并不处于等待状态,因此也不会发出通告。 立即发送 以NSPostNow风格进入队列的通告会在聚结之后,立即发送到通告中心。您可以在不需要异步调用行为的时候 使用NSPostNow风格(或者通过NSNotificationCenter的postNotification:方法来发送)。在很多编程环境下,我们不仅允许同步的行为,而且希望使用这种行为:即您希望通告中心在通告派发之后返回,以便确定观察者对象收到通告并进行了处理。当然,当您希望通过聚结移除队列中类似的通告时,应该用enqueueNotification...方法,且使用NSPostNow风格,而不是使用postNotification:方法。 总结NSNotificationCenter和NSNotificationQueue的区别,也许最大的一点就是发出通知时一个是同步一个是异步。 ------------------------------------------ ### 欢迎加入iOS交流群537774852 如果对您有一定帮助,请关注本人。 (转载请表明出处、作者)