基于 ResponderChain 的對(duì)象交互方式

首先感謝下 Tian Wei Yu一種基于ResponderChain的對(duì)象交互方式 這篇文章酿雪,讓我知道對(duì)象間的交互還有這種姿勢(shì)。說(shuō)實(shí)話,第一遍沒(méi)看懂洽议,自己跟著敲了一遍才理解,所以有了這篇文章漫拭,算是個(gè)記錄亚兄。

前言

Responder Chain ,也就是響應(yīng)鏈采驻,關(guān)于這方面的知識(shí)因?yàn)椴皇潜疚闹攸c(diǎn)审胚,還不太理解的可以去看看這篇文章:史上最詳細(xì)的iOS之事件的傳遞和響應(yīng)機(jī)制-原理篇匈勋。

在 iOS 中,對(duì)象間的交互模式大概有這幾種:直接 property 傳值膳叨、delegate洽洁、KVO、block菲嘴、protocol饿自、多態(tài)、Target-Action 等等龄坪,本文介紹的是一種基于 UIResponder 對(duì)象交互方式昭雌,簡(jiǎn)而言之,就是 通過(guò)在 UIResponder上掛一個(gè) category健田,使得事件和參數(shù)可以沿著 responder chain 逐步傳遞烛卧。對(duì)于那種 subviews 特別多,事件又需要層層傳遞的層級(jí)視圖特別好用妓局,但是总放,缺點(diǎn)也很明顯,必須依賴于 UIResponder 對(duì)象跟磨。

具體事例

我們先來(lái)看看下面這種很常見(jiàn)的界面:

簡(jiǎn)單講解下:最外層是個(gè) UITableView间聊,我們就叫做 SuperTable,每個(gè) cell 里面又嵌套了個(gè) UITableView抵拘,叫做 SubTable哎榴,然后這個(gè) SubTable 的 cell 里面有一些按鈕,我們理一下這個(gè)界面的層級(jí):

UIViewController -> SuperTable -> SuperCell -> SubTable -> SubCell -> UIButton

如果我們需要在最外層的 UIViewController 里捕獲到這些按鈕的點(diǎn)擊事件僵蛛,比如點(diǎn)擊按鈕需要刷新 SuperTable尚蝌,這時(shí)候該怎么實(shí)現(xiàn)呢?

方法有很多充尉,最常見(jiàn)的就是 delegate 飘言,但是因?yàn)閷蛹?jí)太深,導(dǎo)致我們需要一層層的去實(shí)現(xiàn)驼侠,各種 protocol姿鸿、delegate 聲明,很繁瑣倒源,這種時(shí)候苛预,基于 Responder Chain 就很方便了。

具體使用

只需要一個(gè) UIResponder 的 category 就行:

@interface UIResponder (Router)

- (void)routerEventWithSelectorName:(NSString *)selectorName
                     object:(id)object
                   userInfo:(NSDictionary *)userInfo;


@end
@implementation UIResponder (Router)

- (void)routerEventWithSelectorName:(NSString *)selectorName
                             object:(id)object
                           userInfo:(NSDictionary *)userInfo {
    
    [[self nextResponder] routerEventWithSelectorName:selectorName
                                       object:object
                                     userInfo:userInfo];
    
}

@end

最里層 UIButton 的點(diǎn)擊處理:

- (IBAction)btnClick1:(UIButton *)sender {
    
    [self routerEventWithSelectorName:@"btnClick1:userInfo:" object:sender userInfo:@{@"key":@"藍(lán)色按鈕"}];
    
}

外層 UIViewController 的接收:

- (void)routerEventWithSelectorName:(NSString *)selectorName
                     object:(id)object
                   userInfo:(NSDictionary *)userInfo {
        
    SEL action = NSSelectorFromString(selectorName);
    
    NSMutableArray *arr = [NSMutableArray array];
    if(object) {[arr addObject:object];};
    if(userInfo) {[arr addObject:userInfo];};
    
    [self performSelector:action withObjects:arr];

}

事件響應(yīng):

- (void)btnClick1:(UIButton *)btn userInfo:(NSDictionary *)userInfo {
    
    NSLog(@"%@  %@",btn,userInfo);
    
}

如果想在傳遞過(guò)程中新增參數(shù)笋熬,比如想在 SuperCell 這一層加點(diǎn)參數(shù)热某,只需要在對(duì)應(yīng)的地方實(shí)現(xiàn)方法就行:

- (void)routerEventWithSelectorName:(NSString *)selectorName object:(id)object userInfo:(NSDictionary *)userInfo {
    
    NSMutableDictionary *mDict = [userInfo mutableCopy];
    mDict[@"test"] = @"測(cè)試";

    [super routerEventWithSelectorName:selectorName object:object userInfo:[mDict copy]];
}

設(shè)計(jì)思路

- (void)routerEventWithSelectorName:(NSString *)selectorName
                     object:(id)object
                   userInfo:(NSDictionary *)userInfo

細(xì)心的可以發(fā)現(xiàn),我這里直接把 SEL 設(shè)計(jì)成以 NSString 的形式傳遞了,再在外面通過(guò) NSSelectorFromString(selectorName) 轉(zhuǎn)成對(duì)應(yīng)的 SEL昔馋。原文中傳的是個(gè)用來(lái)標(biāo)識(shí)具體是哪個(gè)事件的字串筹吐,還需要維護(hù)專門的 NSDictionary 來(lái)找到對(duì)應(yīng)的事件,我覺(jué)得太麻煩秘遏,但是好處是 @selector(....) 聲明和實(shí)現(xiàn)在一個(gè)地方丘薛,可讀性高,也不容易出現(xiàn)拼寫錯(cuò)誤垄提,導(dǎo)致觸發(fā)不了對(duì)應(yīng)方法的問(wèn)題榔袋,具體怎么設(shè)計(jì),大家見(jiàn)仁見(jiàn)智吧~

關(guān)于參數(shù)的傳遞铡俐,比如我觸發(fā) UITableViewDelegate 中的 didSelectRowAtIndexPath: 方法凰兑,<2 個(gè)參數(shù)的情況,performSelector: 方法也可以滿足审丘,但一旦 >2 個(gè)參數(shù)的話吏够,就不行了,這時(shí)候我們就可以用 NSInvocation 來(lái)實(shí)現(xiàn)滩报,我寫了個(gè)分類锅知,支持傳遞多個(gè)參數(shù),搭配使用很方便:

@interface NSObject (PerformSelector)

- (id)performSelector:(SEL)aSelector withObjects:(NSArray <id> *)objects;

@end
@implementation NSObject (PerformSelector)

- (id)performSelector:(SEL)aSelector
          withObjects:(NSArray <id> *)objects {
    
    //創(chuàng)建簽名對(duì)象
    NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector];
    
    //判斷傳入的方法是否存在
    if (!signature) { //不存在
        //拋出異常
        NSString *info = [NSString stringWithFormat:@"-[%@ %@]:unrecognized selector sent to instance",[self class],NSStringFromSelector(aSelector)];
        @throw [[NSException alloc] initWithName:@"ifelseboyxx remind:" reason:info userInfo:nil];
        return nil;
    }
    
    //創(chuàng)建 NSInvocation 對(duì)象
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    
    //保存方法所屬的對(duì)象
    invocation.target = self;
    invocation.selector = aSelector;

    
    //設(shè)置參數(shù)
    //存在默認(rèn)的 _cmd脓钾、target 兩個(gè)參數(shù)售睹,需剔除
    NSInteger arguments = signature.numberOfArguments - 2;
    
    //誰(shuí)少就遍歷誰(shuí),防止數(shù)組越界
    NSUInteger objectsCount = objects.count;
    NSInteger count = MIN(arguments, objectsCount);
    for (int i = 0; i < count; i++) {
        id obj = objects[i];
        //處理參數(shù)是 NULL 類型的情況
        if ([obj isKindOfClass:[NSNull class]]) {obj = nil;}
        [invocation setArgument:&obj atIndex:i+2];
    }
    
    //調(diào)用
    [invocation invoke];
    
    //獲取返回值
    id res = nil;
    //判斷當(dāng)前方法是否有返回值
    if (signature.methodReturnLength != 0) {
        [invocation getReturnValue:&res];
    }
    return res;
}

@end

最后附上 Demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市可训,隨后出現(xiàn)的幾起案子昌妹,更是在濱河造成了極大的恐慌,老刑警劉巖握截,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件飞崖,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡谨胞,警方通過(guò)查閱死者的電腦和手機(jī)固歪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)胯努,“玉大人牢裳,你說(shuō)我怎么就攤上這事∫杜妫” “怎么了蒲讯?”我有些...
    開(kāi)封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)恬汁。 經(jīng)常有香客問(wèn)我伶椿,道長(zhǎng),這世上最難降的妖魔是什么氓侧? 我笑而不...
    開(kāi)封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任脊另,我火速辦了婚禮,結(jié)果婚禮上约巷,老公的妹妹穿的比我還像新娘偎痛。我一直安慰自己,他們只是感情好独郎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布踩麦。 她就那樣靜靜地躺著,像睡著了一般氓癌。 火紅的嫁衣襯著肌膚如雪谓谦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天贪婉,我揣著相機(jī)與錄音反粥,去河邊找鬼。 笑死疲迂,一個(gè)胖子當(dāng)著我的面吹牛才顿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播尤蒿,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼郑气,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了腰池?” 一聲冷哼從身側(cè)響起尾组,我...
    開(kāi)封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎巩螃,沒(méi)想到半個(gè)月后演怎,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡避乏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年爷耀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拍皮。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歹叮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出铆帽,到底是詐尸還是另有隱情咆耿,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布爹橱,位于F島的核電站萨螺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜慰技,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一椭盏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吻商,春花似錦掏颊、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至柒爸,卻和暖如春准浴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捎稚。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工兄裂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人阳藻。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓晰奖,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親腥泥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子匾南,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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

  • 概述 感謝casa大神的分享:一種基于ResponderChain的對(duì)象交互方式,這里也只是作為筆記來(lái)記錄。閑話少...
    Joshua520閱讀 486評(píng)論 0 0
  • 好奇觸摸事件是如何從屏幕轉(zhuǎn)移到APP內(nèi)的蛔外?困惑于Cell怎么突然不能點(diǎn)擊了蛆楞?糾結(jié)于如何實(shí)現(xiàn)這個(gè)奇葩響應(yīng)需求?亦或是...
    Lotheve閱讀 57,149評(píng)論 51 599
  • 在iOS開(kāi)發(fā)中經(jīng)常會(huì)涉及到觸摸事件夹厌。本想自己總結(jié)一下豹爹,但是遇到了這篇文章,感覺(jué)總結(jié)的已經(jīng)很到位矛纹,特此轉(zhuǎn)載臂聋。作者:L...
    WQ_UESTC閱讀 6,012評(píng)論 4 26
  • Understanding Event Handling, Responders, and the Respond...
    frankisbaby閱讀 412評(píng)論 0 0
  • 在面試中,我們經(jīng)常會(huì)遇到一些原理性的問(wèn)題或南,很常識(shí)但很難用通俗的語(yǔ)言解釋清楚孩等,這也是大部分業(yè)務(wù)級(jí)程序員經(jīng)常失誤的地方...
    歐巴冰冰閱讀 1,921評(píng)論 2 21