iOS 之 MulticastDelegate

前段時(shí)間在研究XMPPFramework的時(shí)候發(fā)現(xiàn)了里面一個(gè)很有趣的特性径缅,MulticastDelegate岸更,也就是多重代理。我們知道iOS開發(fā)中對(duì)象直接常用的溝通方式一般分為Block朴艰,DelegateNotification 其中只有Notification是一對(duì)多的,而DelegateBlock都是一對(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慨蛙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辽聊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子期贫,更是在濱河造成了極大的恐慌跟匆,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件通砍,死亡現(xiàn)場(chǎng)離奇詭異玛臂,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)封孙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門迹冤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人虎忌,你說(shuō)我怎么就攤上這事泡徙。” “怎么了膜蠢?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵堪藐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我挑围,道長(zhǎng)礁竞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任杉辙,我火速辦了婚禮苏章,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘奏瞬。我一直安慰自己枫绅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布硼端。 她就那樣靜靜地躺著并淋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪珍昨。 梳的紋絲不亂的頭發(fā)上县耽,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音镣典,去河邊找鬼兔毙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛兄春,可吹牛的內(nèi)容都是我干的澎剥。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼赶舆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼哑姚!你這毒婦竟也來(lái)了祭饭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤叙量,失蹤者是張志新(化名)和其女友劉穎倡蝙,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绞佩,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寺鸥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了品山。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胆建。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖谆奥,靈堂內(nèi)的尸體忽然破棺而出眼坏,到底是詐尸還是另有隱情,我是刑警寧澤酸些,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布宰译,位于F島的核電站,受9級(jí)特大地震影響魄懂,放射性物質(zhì)發(fā)生泄漏沿侈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一市栗、第九天 我趴在偏房一處隱蔽的房頂上張望缀拭。 院中可真熱鬧,春花似錦填帽、人聲如沸蛛淋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)褐荷。三九已至,卻和暖如春嘹悼,著一層夾襖步出監(jiān)牢的瞬間叛甫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工杨伙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留其监,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓限匣,卻偏偏與公主長(zhǎng)得像抖苦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,130評(píng)論 30 470
  • 史上最全的iOS面試題及答案 iOS面試小貼士———————————————回答好下面的足夠了----------...
    Style_偉閱讀 2,346評(píng)論 0 35
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛閱讀 1,966評(píng)論 0 7
  • 多線程睛约、特別是NSOperation 和 GCD 的內(nèi)部原理鼎俘。運(yùn)行時(shí)機(jī)制的原理和運(yùn)用場(chǎng)景哲身。SDWebImage的原...
    LZM輪回閱讀 2,004評(píng)論 0 12
  • __block和__weak修飾符的區(qū)別其實(shí)是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用辩涝,...
    LZM輪回閱讀 3,291評(píng)論 0 6