25. 什么时候用delegate,什么时候用Notification?
1、Delegate是一种点对点的消息传送机制。传递给自己或者其他对象。有时候他还会返回一个影响事件如何被处理的值。
举一个简单的Mac例子,NSWindow声明了有一个名叫windowShouldClose:的协议,当用户点窗口左上角的关闭按钮时,这个窗口对象就会发送一个windowShouldClose:消息给他的delegate,来询问这个窗口是否可以被关闭。这个delegate会返回一个boolean值,来控制窗口对象的这个(关闭)行为。
2、Notification是一种一对多的消息传递方式。他的实质是广播信息给所有observer。
消息发送者不需要知道谁是消息的接收者。
Delegate:
消息的发送者(sender)告知接收者(receiver)某个事件将要发生,delegate同意然然后发送者响应事件,delegate机制使得接收者可以改变发送者的行为。通常发送者和接收者的关系是直接的一对多的关系。
Notification:
消息的发送者告知接收者事件已经发生或者将要发送,仅此而已,接收者并不能反过来影响发送者的行为。通常发送者和接收者的关系是间接的多对多关系。
1、什么是Notification?
答:观察者模式,controller向defaultNotificationCenter添加自己的 notification,其他类注册这个notification就可以收到通知,这些类可以在收到通知时做自己的操作(多观察者默认随机顺序发通知给 观察者们,而且每个观察者都要等当前的某个观察者的操作做完才能轮到他来操作,可以用NotificationQueue的方式安排观察者的反应顺序,也 可以在添加观察者中设定反映时间,取消观察需要在viewDidUnload 跟dealloc中都要注销)。
2、什么时候用delegate,什么时候用Notification?
答:delegate针对one-to-one关系,并且reciever可以返回值给 sender,notification 可以针对one-to-one/many/none,reciever无法返回值给sender.所以,delegate用于sender希望接受到 reciever的某个功能反馈值,notification用于通知多个object某个事件。
Notification:
通知模式:一个对象能够给其他任意数量的对象广播信息。对象之间可以没有耦合关系。
NSNotification(通知),封装了要广播的信息。 NSNotificationCenter(通知中⼼心),管理注册接收消息对象,广播消息。 observer(观察者),需要监测广播信息的对象,即接收信息的对象。
//www.greatytc.com/p/6107494426e4
//www.greatytc.com/p/393920b15539
NSNotification原理简析
NSNotification使用的是同步操作。即如果你在程序中的A位置post了一个NSNotification,在B位置注册了一个observer,通知发出后,必须等到B位置的通知回调执行完以后才能返回到A处继续往下执行。因此,不要过多的或者低效的使用NSNotification,推荐的方式是通过一些“中间的”观察者将通告的结果传递给它们可以访问的对象。
如果想让NSNotification的post处和observer处异步执行,可以通过NSNotificationQueue实现。
对于同一个通知,如果注册了多个观察者,则这多个观察者的执行顺序和他们的注册顺序是保持一致的。
NSNotification线程
NSNotificationCenter在转发NSNotification消息的时候,在哪个线程中post,就在哪个线程中转发。换句话说,不管你的observer是在哪个线程,observer的回调方法执行线程都和post的线程保持一致。
如果想让post的线程和转发的线程不同,可以通过NSNotification重定向技术实现。
作者:吴白
链接://www.greatytc.com/p/393920b15539
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
http://www.cnblogs.com/xiaouisme/archive/2012/04/06/2434753.html
通告中心同步地将通告派发给它的观察者。发出通告的对象直到所有的通告被发出后,才重新获得程序的控制权。如果需要以异步的方式发送通告,必须使用通告队列。
NSNotificationCenter
每个任务都有一个缺省的通告中心,您可以通过NSNotificationCenter的defaultCenter类方法来进行访问。通告中心在单任务中处理通告。如果需要在同一个机器的不同任务之间进行通讯,可以使用分布式通告中心。
通告中心同步地将通告发送给观察者。换句话说,在发出一个通告时,在所有的观察者接收和处理完成通告之前,程序的控制权不会返回给发送者。如果需要异步发送通告,可以使用通告队列,这在"通告队列"部分中进行描述。
在多线程的应用程序中,通告总是在发送的线程中传送,这个线程可能不同于观察者注册所在的线程。
NSDistributedNotificationCenter
每个任务都有一个缺省的分布式通告中心,您可以通过NSDistributedNotificationCenter的defaultCenter类方法来访问。这个分布式通告中心负责处理同一个机器的不通任务之间的通告。如果需要实现不同机器上的任务间通讯,请使用分布式对象。
发送分布式通告是一个开销昂贵的操作。通告会被发送给一个系统级别的服务器,然后再分发到注册了该分布式通告的对象所在的任务中。发送通告和通告到达另一个任务之间的延迟是很大的。事实上,如果发出的通告太多,以致于充满了服务器的队列,就可能被丢弃。
分布式通告通过任务的运行循环来分发。任务必须运行在某种“常见”模式的运行循环下,比如NSDefaultRunLoopMode模式,才能接收分布式通告。如果接收通告的任务是多线程的,则不要以通告会到达主线程作为前提。通告通常被分发到主线程的运行循环上,但是其它线程也可以接收通告。
尽管常规的通告中心允许任何对象作为通告对象(也就是通告封装的对象),分布式通告中心只支持将NSString对象作为它的通告对象。由于发出通告的对象和通告的观察者可能位于不同的任务中,通告不能包含指向任意对象的指针。因此,分布式通告中心要求通告使用字符串作为通告对象。通告的匹配就是基于这个字符串进行的,而不是基于对象指针。
通告队列
NSNotificationQueue对象(或者简单称为通告队列)的作用是充当通告中心(NSNotificationCenter的实例)的缓冲区。通告队列通常以先进先出(FIFO)的顺序维护通告。当一个通告上升到队列的前面时,队列就将它发送给通告中心,通告中心随后将它派发给所有注册为观察者的对象。
每个线程都有一个缺省的通告队列,与任务的缺省通告中心相关联。图5-9展示了这种关联。您可以创建自己的通告队列,使得每个线程和通告中心有多个队列。
聚结的通告
NSNotificationQueue类为Foundation Kit的通告机制增加了两个重要的特性:即通告的聚结和异步发送。聚结是把和刚进入队列的通告相类似的其它通告从队列中移除的过程。如果一个新的通告和已经在队列中的通告相类似,则新的通告不进入队列,而所有类似的通告(除了队列中的第一个通告以外)都被移除。然而,您不应该依赖于这个特殊的聚结行为。
您可以为enqueueNotification:postingStyle:coalesceMask:forModes:方法的第三个参数指定如下的一或多个常量,指示简化的条件:
NSNotificationNoCoalescing
NSNotificationCoalescingOnName
NSNotificationCoalescingOnSender
您可以对NSNotificationCoalescingOnName和NSNotificationCoalescingOnSender常量进行位或操作,指示Cocoa同时使用通告名称和通告对象进行聚结。那样的话,和刚刚进入队列的通告具有相同名称和发送者对象的所有通告都会被聚结。
异步发送通告
通过NSNotificationCenter类的postNotification:方法及其变体,您可以将通告立即发送给通告中心。但是,这个方法的调用是同步的:即在通告发送对象可以继续执行其所在线程的工作之前,必须等待通告中心将通告派发给所有的观察者并将控制权返回。但是,您也可以通过NSNotificationQueue的enqueueNotification:postingStyle:和enqueueNotification:postingStyle:coalesceMask:forModes:方法将通告放入队列,实现异步发送,在把通告放入队列之后,这些方法会立即将控制权返回给调用对象。
Cocoa根据排队方法中指定的发送风格和运行循环模式来清空通告队列和发送通告。模式参数指定在什么运行循环模式下清空队列。举例来说,如果您指定NSModalPanelRunLoopMode模式,则通告只有当运行循环处于该模式下才会被发送。如果当前运行循环不在该模式下,通告就需要等待,直到下次运行循环进入该模式。
向通告队列发送通告可以有三种风格:NSPostASAP、NSPostWhenIdle、和NSPostNow,这些风格将在接下来的部分中进行说明。
尽快发送
以NSPostASAP风格进入队列的通告会在运行循环的当前迭代完成时被发送给通告中心,如果当前运行循环模式和请求的模式相匹配的话(如果请求的模式和当前模式不同,则通告在进入请求的模式时被发出)。由于运行循环在每个迭代过程中可能进行多个调用分支(callout),所以在当前调用分支退出及控制权返回运行循环时,通告可能被分发,也可能不被分发。其它的调用分支可能先发生,比如定时器或由其它源触发了事件,或者其它异步的通告被分发了。
您通常可以将NSPostASAP风格用于开销昂贵的资源,比如显示服务器。如果在运行循环的一个调用分支过程中有很多客户代码在窗口缓冲区中进行描画,在每次描画之后将缓冲区的内容刷新到显示服务器的开销是很昂贵的。在这种情况下,每个draw...方法都会将诸如“FlushTheServer” 这样的通告排入队列,并指定按名称和对象进行聚结,以及使用NSPostASAP风格。结果,在运行循环的最后,那些通告中只有一个被派发,而窗口缓冲区也只被刷新一次。
空闲时发送
以NSPostWhenIdle风格进入队列的通告只在运行循环处于等待状态时才被发出。在这种状态下,运行循环的输入通道中没有任何事件,包括定时器和异步事件。以NSPostWhenIdle风格进入队列的一个典型的例子是当用户键入文本、而程序的其它地方需要显示文本字节长度的时候。在用户输入每一个字符后都对文本输入框的尺寸进行更新的开销是很大的(而且不是特别有用),特别是当用户快速输入的时候。在这种情况下,Cocoa会在每个字符键入之后,将诸如“ChangeTheDisplayedSize”这样的通告进行排队,同时把聚结开关打开,并使用NSPostWhenIdle风格。当用户停止输入的时候,队列中只有一个“ChangeTheDisplayedSize”通告(由于聚结的原因)会在运行循环进入等待状态时被发出,显示部分也因此被刷新。请注意,运行循环即将退出(当所有的输入通道都过时的时候,会发生这种情况)时并不处于等待状态,因此也不会发出通告。
立即发送
以NSPostNow风格进入队列的通告会在聚结之后,立即发送到通告中心。您可以在不需要异步调用行为的时候 使用NSPostNow风格(或者通过NSNotificationCenter的postNotification:方法来发送)。在很多编程环境下,我们不仅允许同步的行为,而且希望使用这种行为:即您希望通告中心在通告派发之后返回,以便确定观察者对象收到通告并进行了处理。当然,当您希望通过聚结移除队列中类似的通告时,应该用enqueueNotification...方法,且使用NSPostNow风格,而不是使用postNotification:方法。