前段時(shí)間在研究XMPPFramework的時(shí)候發(fā)現(xiàn)了里面一個(gè)很有趣的特性径缅,MulticastDelegate
岸更,也就是多重代理。我們知道iOS開發(fā)中對(duì)象直接常用的溝通方式一般分為Block
朴艰,Delegate
和 Notification
其中只有Notification
是一對(duì)多的,而Delegate
和 Block
都是一對(duì)一的混移。XMPPFramework中對(duì)于 MulticastDelegate
的解釋如下:
The xmpp framework needs to support an unlimited number of extensions. This includes the official extensions that ship with the framework, as well as any number of extensions or custom code you may want to plug into the framework. So the traditional delegate pattern simply won't work. XMPP modules and extensions need to be separated into their own separate classes, yet each of these classes needs to receive delegate methods. And the standard NSNotification architecture won't work either because some of these delegates require a return variable. (Plus it's really annoying to extract parameters from a notification's userInfo dictionary.)
簡(jiǎn)單來(lái)說(shuō)就是XMPPFramework需要支持很多的擴(kuò)展功能祠墅,而且這些擴(kuò)展功能是可以共存的,XMPPFramework 的 Module 需要區(qū)分這些擴(kuò)展歌径,同時(shí)這些擴(kuò)展的代理需要一個(gè)返回值毁嗦,所以也不能用通知,最終使用了多重代理回铛。
XMPPFramework怎么說(shuō)
XMPPFramework中對(duì)MulticastDelegate的具體說(shuō)明分為了以下幾點(diǎn)
delegates and notifications
主要說(shuō)明了代理和通知各自的利弊
| 代理 | 通知
-------------|-------------|-------------
優(yōu)點(diǎn) | 大量回調(diào)方法是書寫方便</p>回調(diào)參數(shù)讀寫方便</p>允許返回值| 允許一對(duì)多傳值
缺點(diǎn) | 只能一對(duì)一傳值</p> | 回調(diào)較多時(shí)注冊(cè)繁瑣</p> 從userInfo中去除參數(shù)繁瑣 </p> 不允許返回值
What are the requirements for XMPPFramework?
1.XMPPFramework需要一對(duì)多廣播消息的的能力
2.方便擴(kuò)展
3.允許返回值
4.線程安全(Socket IO, xml解析, disk IO等)
What's it look like?
客戶端使用MulticastDelegate的方式如下
// Add myself as a delegate, and tell xmppStream to invoke my delegate methods on the main thread
[xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
// Then just implement whatever delegate methods you need like normal
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
...
}
What about return variables?
我們有如下代理方法
- (BOOL)worker:(Worker *)sender shouldPerformSubTask:(id)subtask;
如果有三個(gè)對(duì)象都實(shí)現(xiàn)了這個(gè)代理狗准,其中兩個(gè)返回 YES
,一個(gè)返回 NO
,我們應(yīng)該根據(jù)不同的情況做不同處理茵肃,比如在這里恐疲,我們處理為只要有一個(gè)代理返回NO
棍现,那么我們就不執(zhí)行subtask
缸榄。
那么問(wèn)題就來(lái)了:我們?cè)撊绾螌?shí)現(xiàn)這種代理邏輯呢纠修?
GCDMulticastDelegate
中的每一個(gè)節(jié)點(diǎn)都保存有一個(gè)delegate
和一個(gè)dispatch_queue
,但是我們并不能只是簡(jiǎn)單的遍歷GCDMulticastDelegate
中的節(jié)點(diǎn)您没,原因如下:
假設(shè)我們的對(duì)象運(yùn)行在
dispatch_queue_a
, delegate運(yùn)行在dispatch_queue_b
鸟召,而我們使用dispatch_sync(dispatch_queue_b, block),同時(shí)在block中使用了當(dāng)前對(duì)象的一些屬性氨鹏,那么就會(huì)形成死鎖欧募。
處理返回值的 MulticastDelegate 實(shí)現(xiàn)舉例:
// Delegate rules:
//
// 如果有任意一個(gè)代理返回NO,則result為NO
// 否則result為YES.
SEL selector = @selector(worker:shouldPerformSubTask:);
NSUInteger delegateCount = [multicastDelegate countForSelector:selector];
if (delegateCount == 0)
{
// 沒有代理實(shí)現(xiàn)該方法的時(shí)候默認(rèn)為YES
[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 {
// 等待代理執(zhí)行結(jié)束
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);
}});
}
GCDMulticastDelegate
上面示例中提到了GCDMulticastDelegate
仆抵,也就是多重代理的核心實(shí)現(xiàn)類跟继, XMPPFramework的源碼中可以看到GCDMulticastDelegate
包含分為以下幾部分:
- GCDMulticastDelegateNode
- GCDMulticastDelegateEnumerator
- GCDMulticastDelegate
GCDMulticastDelegateNode
表示代理數(shù)組中的節(jié)點(diǎn),里面包括一個(gè)代理對(duì)象 id delegate
和一個(gè)執(zhí)行代理需要的現(xiàn)成 dispatch_queue_t delegateQueue
肢础。
GCDMulticastDelegateEnumerator
还栓,用于對(duì)當(dāng)前的代理數(shù)組進(jìn)行枚舉操作,屬性有 代理數(shù)組 delegateNodes
, 當(dāng)前遍歷的節(jié)點(diǎn)下標(biāo)currentNodeIndex
传轰,代理數(shù)組總數(shù) numNodes
GCDMulticastDelegate
主要實(shí)現(xiàn)了對(duì)于代理數(shù)組的增刪操作已經(jīng)特殊的統(tǒng)計(jì)操作如:
//返回某個(gè)各項(xiàng)遵循代理的數(shù)量
- (NSUInteger)countOfClass:(Class)aClass
{
NSUInteger count = 0;
for (GCDMulticastDelegateNode *node in delegateNodes)
{
id nodeDelegate = node.delegate;
#if __has_feature(objc_arc_weak) && !TARGET_OS_IPHONE
if (nodeDelegate == [NSNull null])
nodeDelegate = node.unsafeDelegate;
#endif
if ([nodeDelegate isKindOfClass:aClass])
{
count++;
}
}
return count;
}
//返回實(shí)現(xiàn)了某個(gè)選擇子的代理的數(shù)量
- (NSUInteger)countForSelector:(SEL)aSelector
{
NSUInteger count = 0;
for (GCDMulticastDelegateNode *node in delegateNodes)
{
id nodeDelegate = node.delegate;
#if __has_feature(objc_arc_weak) && !TARGET_OS_IPHONE
if (nodeDelegate == [NSNull null])
nodeDelegate = node.unsafeDelegate;
#endif
if ([nodeDelegate respondsToSelector:aSelector])
{
count++;
}
}
return count;
}
綜上所述,當(dāng)我們遇到對(duì)象一對(duì)多傳值谷婆,同時(shí)需要一個(gè)返回值的時(shí)候就可以使用MulticastDelegate
慨蛙。