该文档回答了这几个问题:(1)什么是多重委托?(2)使用的原因。(3)为什么不使用普通的委托与通知?
介绍
苹果有两种常用的回调方式:
委托
通知 委托非常简单和直接,用户注册自己为一个委托,然后实现它需要去实现的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 [worker setDelegate:self ]; -(void )wokerDidFinish:(Worker *)sender { } -(void )woker:(Worker *)sender didFinishSubTask:(id )subtask inDuration:(NsTimeInterval)elapsed { } -(BOOL )worker:(Worker *)sender shouldPerformSubTask:(id )subtask { }
通知同样简单,但是需要多了几步,用户需要分别注册它们感兴趣的每个通知:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector (workerDidFinish:) name:WorkerDidFinishNotification object:nil ]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector (workerDidFinishSubTask:) name:WorkerDidFinishSubTaskNotification object:nil ]; - (void )workerDidFinish:(NSNotification *)notification { Worker *sender = [notification object]; } - (void )workerDidFinishSubTask:(NSNotification *)notification { Worker *sender = [notification object]; id subtask = [[notification userInfo] objectForKey:@"subtask" ];NSTimeInterval elapsed = [[[notification userInfo] objectForKey"duration" ] doubleValue];}
其中我们要将参数从通知字典中取出,也就是说用户需要知道正确的键值才能取出对应的参数。 另外第三方委托方法不能通过通知来实现,因为通知不允许返回变量 。 Delegate的缺点:
只能有一个委托 notification的缺点:
注册多个回调时比较麻烦
从字典中取出参数非常麻烦
当需要返回值时无法使用XMPP框架需要哪些东西?
xmpp框架必须能够把事件广播给多个接收者。 以消息为例,可能存在多个接收端:聊天窗口,历史日志以及消息推送系统。
xmpp框架必须易于扩展。 它必须能够支持大量的EXP’s以及任何想进行二次开发的xmpp协议的开发者,我们选择的解决方案必须在传播端和接收端都易于使用。
必须支持返回值。 一个不错的例子是XMPP RFC的IQ处理授权。如果一个客户端接收到一个类型为’get’或’set’的IQ,但是不知道该怎么处理时,它必须返回一个类型为’error’的IQ,这在多插件时非常有用。
必须有效维持线程安全。 xmpp框架存在大量并行,Socket接口、xml解析、xmpp、模块、磁盘接口以及委托都能够运行在他们自己的GCD队列中,这在多核设备中意味着许多任务同时运行在不同的线程中,系统不应该通过跳出循环来维持线程安全。
从这些看来委托和通知都不能很好的满足我们的要求,所以我们设计了GCDMulticastDelegate
类。
如何运作
它非常简单,作为客户端,你只要这样:1 2 3 4 5 6 7 8 [xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; -(void )smppStream:(XMPPStream *)sender didReciveMessage:(XMPPMessage *)message { ... }
就是这些,你可以看到,它和普通的委托非常相似,但是允许你指定特定线程。 如果之后你决定把你的操作移出主线程的话,同样非常简单:
1 2 3 4 [xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; [xmppSream addDelegate:bgProcessor delegateQueue:bgProcessorQueue];
在IPhone这样的设备环境下,这样做可以很好的维持你应用的性能。
如何在自己的插件中使用
为了使用多重委托,作为广播端,你需要申明并初始化它: GCDMulticastDelegate multicastDelegate; multicastDelegate = (GCDMulticastDelegate )[[GCDMulticastDelegate alloc] init]; 然后添加能够允许其他对象从委托列表中添加/删除委托的方法:
1 2 3 4 5 6 7 8 9 -(void )addDelegate:(id )delegate delegateQueue:(dispatch_queue_t )delegateQueue { [multicastDelegate addDelegate:delegate delegateQueue:delegateQueue]; } -(void )removeDelegate:(id )delegate delegateQueue:(dispatch_queue_t )delegateQueue { [multicastDelegate removeDelegate:delelgate delegateQueue:delegateQueue]; }
当你要触发某个委托方法时,只要是注册了的委托,你可以: [multicastDelegate worker:self didFinishSubTask:subtask inDyration:elapsed];
关于返回变量
对于下面的这个委托方法: -(BOOL)worker:(Worker *)sender shouldPerformSubTask:(id)subtask;
假如返回了三个委托,两个返回YES另一个返回NO,那如何处理它? 一般来说,如果任何一个委托返回NO,那我们就不能执行这个任务。 那么如何实现它?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 SEL selector = @selector (worker:shouldPerformSubTask:); NSUInteger delegateCount = [multicastDelegate countForSelector:selector];if (delegateCount == 0 ){ [self continuePerformSubTask:YES ]; } else { GCDMulticastDelegateEnumerator *delegateEnumerator = [multicastDelegate delegateEnumerator]; dispatch_semaphore_t delSemaphore = dispatch_semaphore_create(0 ); dispatch_group_t delGroup = dispatch_group_create(); id del;dispatch_queue_t dq;while ([delegateEnumerator getNextDelegate:&del delegateQueue:&dq forSelector:selector]){ dispatch_group_async(delGroup, dq, ^{ @autoreleasepool { if (![del worker:self shouldPerformSubTask:subtask]){ dispatch_semaphore_signal(delSemaphore); } }}); } dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 );dispatch_async (concurrentQueue, ^{ @autoreleasepool {dispatch_group_wait(delGroup, DISPATCH_TIME_FOREVER); BOOL shouldPerformSubTask = (dispatch_semaphore_wait(delSemaphore, DISPATCH_TIME_NOW) != 0 );dispatch_async (ourQueue, ^{ @autoreleasepool {[self continuePerformSubTask:shouldPerformSubTask]; }}); dispatch_release(delSemaphore); dispatch_release(delGroup); }}); }