iOS中的三种路由方案实践

原文地址
iOS中的三种路由方案实践
https://www.jianshu.com/p/72d705ecc177

日常开发中,你可能会遇到这样的问题,各个模块相互引用,相互依赖,直观表现就是引入了很多头文件,很混乱,比如登录收藏消息模块间的相互调用,这样的代码显然不能符合 低耦合、高内聚、职责单一逻辑清晰的代码设计原则。
为此,我们可能需要使用一个 调度中心 去管理这些模块,有了这个调度中心,每个模块就不需要依赖其它模块,不用导入其它模块的头文件了,只需要调度中心关心每个模块的调度,其它模块只需要关心怎么调用和调用后反馈的结果,这个调度中心就是 路由(Router), 这种设计模式也被称为 中介者模式

下面我将从以下几个方面来介绍一下我学习到的关于路由的知识:

  1. 路由是什么
  2. 路由和组件化的关系
  3. 业界常见的三种解决方案
  4. 总结

路由是什么

路由简单来说就是一个中转站,输入一条数据,然后按照内部的一定规则,处理这条数据,最后输出特定格式的新数据。

1.网络层

从网络的角度来说,路由就是指路由器从一个接口收到数据包,根据数据路由包的目的地址进行定向并转发到另一个接口的过程。

2.服务器端

从服务器角度来说,当接收到客户端发来的 HTTP 请求时,会根据请求的URL,来找到相应的映射函数,然后执行该函数,并将函数的返回值发送给客户端。 在WEB开发中,路由就是URL函数的映射,在WEB开发中,Route是指根据URL找到处理这个URL函数Router 可以理解为一个容器,或者说是一种机制,它管理了一组Route

3.移动端

从移动端的角度来说,就是把URL映射到相应的上,iOS中的路由不是仅仅做页面跳转的,只是用的最多的地方,主要是因为可以解耦页面跳转的逻辑,它可以中转的不仅仅是 Controller还可以是其它对象,比如 UIImage

路由和组件化的关系

路由是一个很好的解耦的方案,它可以为各个组件之间调用提供遍历,没有路由,组件化也是可以用的。

业界常见的三种解决方案

1.Url-scheme注册(MGJRouter)

iOS系统中默认是支持 url scheme方式的,例如可以在浏览器中输入: weixin:// 就可以打开微信应用。自然在APP内部也可以通过这种方法来实现组件之间的路由设计。

这种方式实现的原理是在APP启动的时候,或者是向以下实例中的每个模块自己的load方法里面注册自己的断链(url),以及对外提供服务(Block),通过url-scheme标记好,然后维护在url-router里面。 url-router中保存了各个组件对应的url-scheme,只要其它组件调用了 open url 的方法,url-router就会去根据url去查找对应的服务并执行,详见demo。

1.1 URL的命名规范

遵循网上通用的 URIweb service模式的资源通用表示方式) 的格式,例如 appscheme://pathctd://home/scan

1.2 常见案例

1.2.1 JLRoutes git 上 star 最多的一个开源库

本质可以理解为保存一个全局的mapkeyurl,value是对应存放的block数组urlblock都会常驻在内存中,当打开一个url时,GLRoutes就可以遍历这个全局的map,通过url来执行对应的block

1.2.2 routable-ios
1.2.3 HHRouter
1.2.4 MGJRouter

蘑菇街的技术团队开源的一个router,特点是使用简单方便,JLRoutes的问题主要在于查找url的实现不够高效,通过遍历而不是匹配。还有就是功能偏多。HHRouterurl查找是基于匹配,所以会更高效,MGJRouter也是采用的这种方法,但它和viewcontroller绑定地过于紧密,一定程度上降低了灵活性。于是就有了 MGJrouter, 从数据结构上看它和 HHRouter是一样的。

蘑菇街方案不好的地方:

  1. URL注册对于实施组件化是完全没有必要的,拓展性和可维护性都降低;
  2. 基于 Open-url 的方案的话,有一个致命缺陷:非常规对象无法参与本地组件间调度;但是可以通过传递parms来解决,但是这个区分了远程调用和本地调用的接口;
  3. 模块内部是否仍然需要使用URL去完成调度?是没有必要的,为啥要复杂化?
  4. 当组件多起来的时候,需要提供一个关乎URL和服务的对应表,并且需要开发人员对这样一个表进行维护;
  5. 这种方式需要在APP启动时,每个组件需要到路由管理中心注册自己的URL及服务,因此 内存中需要保存这样一份表,当组件多起来以后就会出现一些内存的问题
  6. 混淆了本地调用和远程调用,它们的处理逻辑是不同的,正确的做法应该是把远程调用通过一个中间层转化成本地调用,如果把两者混为一谈,后期可能会出现无法区分业务的情况。比如对于组件无法响应的问题,远程调用可能直接显示一个404页面,但是本地调用可能需要做其它处理。如果不加以区分,那么就无法完成这种业务要求。 远程调用只能传递被序列化JSON的数据,像UIImage这样非常规的对象是不行的,所以如果组件接口要考虑远程调用,这里的参数与就不能是这类非常规对象

2.利用Runtime实现的target-action方式(CTMediator)- 推荐

相较于url-scheme的方式进行组件间的路由,runtime的方式借助了OC运行时的特征,实现了组件间服务的自动发现,无需注册即可实现组件间的调用,因此,不管从维护性、可读性、扩展性来说,都是一个比较完美的方案, 详见demo。

原理:

传统的中介者模式,这个中间件Mediator会依赖其它组件,其它组件也会依赖mediator, 但是能不能让mediator不在依赖组件,各个组件之间不再依赖,组件间调用只依赖中间者mediator
casa 大神是这样优化的:
利用target-action的方式,创建一个taget的类,里面定义了一些action方法,这些方法的结果是返回一个controller或其它object,再给中间件CTMedator添加一个分类方法,定义组件外部可调用的方法接口,内部实现方法 perform:target:action的方法,主要通过runtime中的 NSClassFromString 获取target类和 NSSelectorFromString 获取方法名,这样就可以执行先去创建的 target类中的方法得到返回值,再通过分类中的方法传值出去,完美解决~

3.protcol-class注册

通过协议绑定,核心思想和代理传值是一样的,遵循协议,实现协议中的方法,详见demo。

主要思路
1、创建一个头文件 CommonProtocol.h ,里面存放各个模块提供的协议。在各个模块依赖这个头文件,实现协议的方法。
2、创建一个中间类 ProtocolMediator, 提供模块的注册和获取模块的功能(其实就是将类和协议名进行绑定,放在一个字典里,key是协议名字符串,value是类)。
3、在各个模块中实现协议,核心代码如下:

Class cls = [[ProtocolMediator sharedInstance] classForProtocol:@protocol(B_VC_Protocol)];   
UIViewController *B_VC = [[cls alloc] init];
[B_VC action_B:@"param1" para2:222 para3:333 para4:444];
[self presentViewController:B_VC animated:YES completion:nil];

参考文章

1、⾮常全⾯的讲解了各大路由方案
2
3

总结

通过这次对路由方案的研究,认识到了自己对系统架构方面的认识还是太少,以前并没有认真考虑怎么去设计一个好代码,我们需要的事写一些优质代码,牢记 低耦合、高内聚、职责单一逻辑清晰,不能甘心做码农,只知道堆代码!!!

本文Demo地址:点我 CXRouterDemo

4人点赞

iOS

作者:georrychen
链接:https://www.jianshu.com/p/72d705ecc177
来源:
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(iOS中的三种路由方案实践)