基于ResponderChain的对象交互方式

概述

感谢casa大神的分享:一种基于ResponderChain的对象交互方式,这里也只是作为笔记来记录。闲话少叙,进入正文。

前言

众所周知,传统iOS的对象间交互模式就那么几种:直接property传值、delegate、KVO、block、protocol、多态、Target-Action。本文主要介绍基于ResponderChain来实现对象间交互。

这种方式通过在UIResponder上挂一个category,使得事件和参数可以沿着responder chain逐步传递。

这相当于借用responder chain实现了一个自己的事件传递链。这在事件需要层层传递的时候特别好用,然而这种对象交互方式的有效场景仅限于在responder chain上的UIResponder对象上。

实现

仅需要一个category即可实现基于ResponderChain的对象间交互。

.h文件

#import <UIKit/UIKit.h>

@interface UIResponder (Router)

- (void)routerEventWithName:(NSString *)eventName userInfo:(NSDictionary *)userInfo;

@end

.m文件

#import "UIResponder+Router.h"

@implementation UIResponder (Router)


- (void)routerEventWithName:(NSString *)eventName userInfo:(NSDictionary *)userInfo {
    [[self nextResponder] routerEventWithName:eventName userInfo:userInfo];
}

@end

发送事件时

[self routerEventWithName:kTHCellClickedEvent userInfo:@{@"key":@"clicked",@"value":@"2"}];

相应事件时

#pragma mark - event response
- (void)routerEventWithName:(NSString *)eventName userInfo:(NSDictionary *)userInfo
{

    /*
        do things you want
    */

    // 如果需要让事件继续往上传递,则调用下面的语句
    // [super routerEventWithName:eventName userInfo:userInfo];
}

结合策略模式进行更好的处理

在上面的Demo中,如果事件来源有多个,那就无法避免需要if-else语句来针对具体事件作相应的处理。这种情况下,会导致if-else语句极多。所以,可以考虑采用strategy模式来消除if-else语句。

#pragma mark - event response
- (void)routerEventWithName:(NSString *)eventName userInfo:(NSDictionary *)userInfo
{

    NSInvocation *invocation = self.eventStrategy[eventName];
    [invocation setArgument:&userInfo atIndex:2];
    [invocation invoke];

    // 如果需要让事件继续往上传递,则调用下面的语句
    // [super routerEventWithName:eventName userInfo:userInfo];
}

self.eventStrategy是一个字典,这个字典以eventName作key,对应的处理逻辑封装成NSInvocation来做value。

- (NSDictionary <NSString *, NSInvocation *> *)eventStrategy
{
    if (_eventStrategy == nil) {
        _eventStrategy = @{
                               kTHCellClickedEvent: [self createInvocationWithSelector:@selector(ticketEvent:)]
                               };
    }
    return _eventStrategy;
}

博客中casa大神没有给出createInvocationWithSelector的实现,我在这里给出我的实现:

#import "NSObject+Invotation.h"

@implementation NSObject (Invotation)

- (NSInvocation *)createInvocationWithSelector:(SEL)aSelector {
    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:@"remind:" reason:info userInfo:nil];
        return nil;
    }
    
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = self;
    invocation.selector = aSelector;
    
    return  invocation;
}

@end

在这种场合下使用Strategy模式,即可避免多事件处理场景下导致的冗长if-else语句。

结合Decorator模式

在事件层层向上传递的时候,每一层都可以往UserInfo这个dictionary中添加数据。那么到了最终事件处理的时候,就能收集到各层综合得到的数据,从而完成最终的事件处理。

#pragma mark - event response
- (void)routerEventWithName:(NSString *)eventName userInfo:(NSDictionary *)userInfo
{
    NSMutableDictionary *decoratedUserInfo = [[NSMutableDictionary alloc] initWithDictionary:userInfo];
    decoratedUserInfo[@"newParam"] = @"new param"; // 添加数据
    [super routerEventWithName:eventName userInfo:decoratedUserInfo]; // 往上继续传递
}

分析基于ReponderChain的对象交互方式

这种对象交互方式的缺点显而易见,它只能对存在于Reponder Chain上的UIResponder对象起作用。

优点倒是也有蛮多:

以前靠delegate层层传递的方案,可以改为这种基于Responder Chain的方式来传递。在复杂UI层级的页面中,这种方式可以避免无谓的delegate声明。
由于众多自定义事件都通过这种方式做了传递,就使得事件处理的逻辑得到归拢。在这个方法里面下断点就能够管理所有的事件处理。
使用Strategy模式优化之后,UIViewController/UIView的事件响应逻辑得到了很好的管理,响应逻辑不会零散到各处地方。
在此基础上使用Decorator模式,能够更加有序地收集、归拢数据,降低了工程的维护成本。

基于ResponderChain的对象交互方式的适用场景首先要求事件的产生和处理的对象都必须在Responder Chain上,这一点前面已经说过,我就不再赘述了。

它的适用场景还有一个值得说的地方,就是它可以无视命名域的存在。如果采用传统的delegate层层传递的方式,由于delegate需要protocol的声明,因此就无法做到命名域隔离。但如果走Responder Chain,即使是另一个UI组件产生了事件,这个事件就可以被传递到其他组件的UI上。

举个例子:XXXViewController属于A组件,这个UIViewController的view上添加了B组件的某个YYView。那么YYView的事件在通过Responder Chain被XXXViewController处理的时候,就可以不必依赖B组件的YYView了。当然,如果事件本身传递了只有B组件才有的对象,无视命名域这个优点就没有了,不过这种场景在实际业务中其实也不多。

在这种场景下就做到了UI展示与事件处理的分离,事件处理的代码就可以归拢到另外一个对象中去了,使得C的代码量得以减少。

或许可以算是一种新的架构模式:MVCE(Modle View Controller Event)?其实名字、模式什么的都已经不重要了,毕竟所有的架构模式都是脱胎于MVC的,作不同程度的拆解罢了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容

  • 首先感谢下 Tian Wei Yu 的 一种基于ResponderChain的对象交互方式 这篇文章,让我知道对象...
    ifelseboyxx阅读 2,346评论 0 27
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,654评论 18 139
  • 好奇触摸事件是如何从屏幕转移到APP内的?困惑于Cell怎么突然不能点击了?纠结于如何实现这个奇葩响应需求?亦或是...
    Lotheve阅读 57,082评论 51 599
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,967评论 6 13
  • 在iOS开发中经常会涉及到触摸事件。本想自己总结一下,但是遇到了这篇文章,感觉总结的已经很到位,特此转载。作者:L...
    WQ_UESTC阅读 6,010评论 4 26