iOS使用核心的50行代码撸一个路由组件

使用组件化是为了解耦处理,多个模块之间通过协议进行交互。而负责解析协议,找到目的控制器,或者是返回对象给调用者的这个组件就是路由组件。本文讲解如何使用核心的50行代码实现一个路由组件。

  • 组件化和路由

  • 路由的实现

路由注册实现
路由使用实现

  • 客户端的使用

  • 一些小想法

组件化和路由

之前看过挺多的关于路由管理、路由处理的文章,常常会和组件化出现在一起,一开始不知道为何路由和组件化出现在一起,后来公司的项目中使用了路由组件(他本身也是一个组件,确切的说是一个中间人或者中介者),才突然想明白了,原来如此。

使用组件化是为了解耦处理,多个模块之间通过协议进行交互。而负责解析协议,找到目的控制器,或者是返回对象给调用者的这个组件就是路由组件。

路由组件的职责主要是:

  • 给注册者提供注册接口

注册者传递path和path对应的block,block的具体实现又注册者自己处理

  • 给调用者提供使用接口

调用者最简单可以传递一个path给路由组件发起调用,路由组件会把具体的处理转发给注册者,理论上是可以任意的操作,包括页面跳转、弹窗提示、返回一个值给调用者等

下面会会在以上分析的基础上实现一个简单的路由组件,对应的代码可以在YTRouterDemo这里找到

路由的实现

路由的实现包括两部分:路由注册实现以及路由使用实现

路由注册实现

路由注册实现时序图:

如上图所示,步骤很简单:

  • 初始化一个YTRouterActionObject对象,用于保存path和对应的blok
  • 获取到路径对应的节点,path会使用"/"符拆分为多个pathItem,每个pathItem都会保存在一个Dictionary对应的位置上,subRouterMapWithPath负责深度遍历Dictionary,然后找到对应的位置
  • 把YTRouterActionObject对象保存在上一步找到的位置中

以上步骤对应的代码如下:

- (void)registerPath:(NSString *)path actionBlock:(RouterActionBlock)actionBlock {
 YTRouterActionObject *actionObject = [YTRouterActionObject new];
 actionObject.path = path;
 actionObject.actionBlock = actionBlock;
 NSMutableDictionary *subRouter = [self subRouterMapWithPath:path];
 subRouter[YTRouterActionObjectKey] = actionObject;
}
- (NSMutableDictionary *)subRouterMapWithPath:(NSString *)path {
 NSArray *components = [path componentsSeparatedByString:@"/"];
 NSMutableDictionary *subRouter = self.routerMap;
 for (NSString *component in components) {
  if (component.length == 0) {
   continue;
  }
  if (!subRouter[component]) {
   subRouter[component] = [NSMutableDictionary new];
  }
  subRouter = subRouter[component];
 }
 return subRouter;
}

在Demo中注册的几个路由最终的配置如下,比如home/messagelist对应的路由配置保存在<YTRouterActionObject: 0x6040000365e0>对象中

Printing description of self->_routerMap:
{
 home =  {
  "_" = "<YTRouterActionObject: 0x60c00003b040>";
  messagelist =   {
   "_" = "<YTRouterActionObject: 0x6040000365e0>";
   detail =    {
    "_" = "<YTRouterActionObject: 0x600000038ec0>";
   };
   getmessage =    {
    "_" = "<YTRouterActionObject: 0x600000038e80>";
   };
  };
 };
}

路由使用实现

路由使用实现时序图:

如上图所示,步骤很简单:

从注册的配置中找到匹配的YTRouterActionObject对象

执行YTRouterActionObject对象的actionBlock,会传递一个YTRouterActionCallbackObject对象,如果调用者需要的是返回值,可以使用YTRouterActionCallbackObject对象的actionCallbackBlock传递一个返回值,这个actionBlock是又业务方的注册者实现的

以上步骤对应的代码如下:

- (BOOL)runWithActionCallbackObject:(YTRouterActionCallbackObject *)actionCallbackObject {
 // 判断是否支持scheme
 if (![self canAcceptScheme:actionCallbackObject.uri.scheme]) {
  return NO;
 }
 // 获取path对应的ActionObject
 YTRouterActionObject *actionObject = [self actionObjectWithPath:actionCallbackObject.uri.path];
 // 执行Path注册的对应Block
 !actionObject.actionBlock ?: actionObject.actionBlock(actionCallbackObject);
 return YES;
}
- (YTRouterActionObject *)actionObjectWithPath:(NSString *)path {
 NSMutableDictionary *subRouter = [self subRouterMapWithPath:path];
 return subRouter[YTRouterActionObjectKey];
}

客户端的使用

以上讲到了核心的路由注册实现和路由使用实现,总共代码还没有50行,所以还是很简单的,接下来会讲下客户端的使用步骤,包括

  • 客户端注册者注册
  • 客户端调用者使用

客户端注册者注册

注册的时机需要比较找,考虑到集成的方便,选择在load方法中处理路由注册,如下代码所示,添加了几个测试的路由,分两种情况来说明下使用

1、不需要返回值

如下注册"home/messagelist"的是一个页面跳转的路由,actionBlock的参数是一个YTRouterActionCallbackObject对象,可以从YTRouterActionCallbackObject对象或者到参数,关于如何传递值,会在下面的客户端调用者使用这里讲到。然后在actionBlock处理目的页面的初始化、参数设置等步骤,然后执行页面跳转。

2、需要返回值

如下注册"home/messagelist/getmessage"的是一个提供返回值的路由,同样也可以从YTRouterActionCallbackObject对象获取参数,另外YTRouterActionCallbackObject对象还有一个actionCallbackBlock属性是专门处理返回参数给调用者的,如下的代码只是简单返回一个字符串,在更加具体的业务场景中,这里会设置接口调用、数据库查询等任务,最后把结果返回。

@implementation ModuleAUriRegister
+ (void)load {
 [[YTRouterManager sharedRouterManager] registerPath:@"home/messagelist" actionBlock:^(YTRouterActionCallbackObject *callbackObject) {
  MessageListViewController *messageListVC = [MessageListViewController new];
  NSString *title = callbackObject.uri.params[@"title"];
  messageListVC.title = title;
  [[UIViewController yt_currentViewControlloer].navigationController pushViewController:messageListVC animated:YES];;
 }];
 [[YTRouterManager sharedRouterManager] registerPath:@"home/" actionBlock:^(YTRouterActionCallbackObject *callbackObject) {
 }];
 [[YTRouterManager sharedRouterManager] registerPath:@"home/messagelist/detail" actionBlock:^(YTRouterActionCallbackObject *callbackObject) {
 }];
 [[YTRouterManager sharedRouterManager] registerPath:@"home/messagelist/getmessage" actionBlock:^(YTRouterActionCallbackObject *callbackObject) {
  // 内容回调
  !callbackObject.actionCallbackBlock ?: callbackObject.actionCallbackBlock(@"message content text demo");
 }];
}
@end

客户端调用者使用

1、简单的path跳转调用

使用YTRouterManager单例对象的runWithPath方法,传递一个注册的path参数完成跳转。

[self addActionWithTitle:@"Router页面跳转" detailText:@"home/messagelist" callback:^{
 [[YTRouterManager sharedRouterManager] runWithPath:@"home/messagelist"];
}];

2、使用URL调用和有URL参数的调用

使用YTRouterManager单例对象的runWithURLString方法,传递一个完整的包含了scheme/path,或者有参数的会才有参数的URL,比如"YTRouter://home/messagelist" 和 "YTRouter://home/messagelist?title=Hello Message" ,路由组件会解析出里面的scheme、path、params,进行scheme过滤处理、path查询YTRouterActionObject对象处理、参数传递处理。

[self addActionWithTitle:@"Router使用URL调用" detailText:@"YTRouter://home/messagelist" callback:^{
 [[YTRouterManager sharedRouterManager] runWithURLString:@"YTRouter://home/messagelist"];
}];
[self addActionWithTitle:@"Router使用带参数的URL调用" detailText:@"YTRouter://home/messagelist?title=Hello Message" callback:^{
 [[YTRouterManager sharedRouterManager] runWithURLString:@"YTRouter://home/messagelist?title=Hello Message"];
}];

效果如下图所示:

效果图

3、简单的path跳转调用

使用YTRouterManager单例对象的runWithActionCallbackObject方法,传递一个YTRouterActionCallbackObject类型的参数,设置YTRouterActionCallbackObject对象的uri和结果回调actionCallbackBlock参数,在actionCallbackBlock中处理返回值。

[self addActionWithTitle:@"Router获取返回值" detailText:@"home/messagelist/getmessage" callback:^{
 __block id message = nil;
 YTRouterActionCallbackObject *actionCallbackObject = [YTRouterActionCallbackObject new];
 actionCallbackObject.uri = [[YTUri alloc] initWithPath:@"home/messagelist/getmessage"];
 actionCallbackObject.actionCallbackBlock = ^(id result) {
  message = result;
 };
 [[YTRouterManager sharedRouterManager] runWithActionCallbackObject:actionCallbackObject];

 NSLog(@"message = %@", message);
}];

一些小想法

  • load方法中注册path对性能有一定的影响,如果这里会成为性能瓶颈,考虑把这部分分代码放在对象方法中初始化,比如主模块发送消息给各个模块,然后在各个模块中处理注册
  • YTRouterActionObject 如果需要更高的细嫩,可以考虑把path参数解析为components进行缓存,这是一种以空间换时间的策略为了提高查找的效率,使用Dictionary而不是数组保存RouterActionObject
  • 为了提高查找的效率,使用Dictionary而不是数组保存RouterActionObject

总结

以上所述是小编给大家介绍的iOS使用核心的50行代码撸一个路由组件,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • iOS 模块化之JLRoute路由示例

    JLRoutes是一个调用极少代码 , 可以很方便的处理不同URL schemes以及解析它们的参数,并通过回调block来处理URL对应的操作 , 可以用于处理复杂跳转逻辑的三方库. 1.在日常开发中 , push , present 出现在整个程序的各个地方 , 如果你想快速理清一个项目的整体逻辑 , 非常麻烦 . 大多数情况 , 你得找到代码目录 ,根据层级结构分出关系 , 然后找到对应的push位置 , 寻找下一级页面 , 如果本身项目的目录就非常乱 , 那么如果要了解一个项目的整体跳转

  • iOS撸一个简单路由Router的实现代码

    平常开发中用户点击头像, 进入个人主页,这看似平常的操作, 背后极有可能会牵扯到多个模块. 再如: 视频模块的播放页, 有与视频相关的音乐,点击这些音乐,需要跳转到音乐模块的播放页, 这样视频与音乐模块之间,不可避免的会产生依赖或耦合. 这个问题让人脑壳疼,相信很多朋友都这样做过,写一些代理或通知, 不停的传递事件: 有时干脆直接导入另一个模块. 因为我在公司独立开发, 顾及少一点,可以拿公司项目做实践,在尝试组件化的过程中, 了解到了路由, 对于解决上述问题, 有极大的帮助.因此我想总结并与大

  • iOS路由(MGJRouter)的实现

    背景 最开始想做路由,是因为当时app中有大量与H5之间的交互,原生和H5的跳转操作比较多比较频繁,新增一个跳转又涉及到改代码发版本,为了统一iOS.安卓和H5的跳转,引入了路由. 作用 后来发现路由,还可很多作用.Router就像是个调度中心,各个模块通过路由调度其他模块,模块之间不需要相互引用,调度方式更加统一,更加自由,能够实现解耦的作用,同时也为之后的组件化开发提供了基础. 路由选择 目前github优秀的路由设计已经有很多,如JLRoutes,MGJRouter,CTMediator.

  • iOS开发之拦截URL转换成本地路由模块URLRewrite详解

    本文主要给大家介绍了关于iOS拦截URL转换成本地路由模块URLRewrite的相关内容,分享出来供各位iOS开发者们参考学习,下面话不多说了,来一起看看详细的介绍: 需求场景 做过电商App的可能都遇到过这样的需求,在商场首页,各种各样动态的跳转,跳转商品详情.秒杀列表.品牌列表.搜索结果.分类结果页面等等等等.同一个位置,可能今天跳这个商品,明天跳转那个商品,运营配的就是一个web端的URL. 拦截webView里面的URL. 需求分析 拦截各种各样的URL,跳转到指定的原生页面. URL的

  • Vue+axios 实现http拦截及路由拦截实例

    现如今,每个前端对于Vue都不会陌生,Vue框架是如今最流行的前端框架之一,其势头直追react.最近我用vue做了一个项目,下面便是我从中取得的一点收获. 基于现在用vue+webpack搭建项目的文档已经有很多了,我就不再累述了. 技术栈 vue2.0 vue-router axios 拦截器 首先我们要明白设置拦截器的目的是什么,当我们需要统一处理http请求和响应时我们通过设置拦截器处理方便很多. 这个项目我引入了element ui框架,所以我是结合element中loading和me

  • 如何在iOS上使用MVVM进行路由详解

    前言 我已经在几个项目中使用MVVM了一段时间,我真的很喜欢它的简单性.特别是,如果你像许多人一样从MVC迁移,你只需要在你的架构中增加一层ViewModel.如果您发现太多层级造成的复杂,这确实使事情变得更容易. 这是一个良好的开端,但这种简单并不总是好的.在MVVM中,您将业务逻辑移出视图控制器(VC),然后意识到它仍然很胖.视图模型(VM)现在具有业务逻辑,但是展示数据(格式化)或路由如何?他们仍然被困在VC中,我们需要将它们移出. #示例流程 假设我们正在实现登陆页面,如下所示. ##路

  • iOS使用核心的50行代码撸一个路由组件

    使用组件化是为了解耦处理,多个模块之间通过协议进行交互.而负责解析协议,找到目的控制器,或者是返回对象给调用者的这个组件就是路由组件.本文讲解如何使用核心的50行代码实现一个路由组件. 组件化和路由 路由的实现 路由注册实现 路由使用实现 客户端的使用 一些小想法 组件化和路由 之前看过挺多的关于路由管理.路由处理的文章,常常会和组件化出现在一起,一开始不知道为何路由和组件化出现在一起,后来公司的项目中使用了路由组件(他本身也是一个组件,确切的说是一个中间人或者中介者),才突然想明白了,原来如此

  • 仅用50行代码实现一个Python编写的计算器的教程

     简介 在这篇文章中,我将向大家演示怎样向一个通用计算器一样解析并计算一个四则运算表达式.当我们结束的时候,我们将得到一个可以处理诸如 1+2*-(-3+2)/5.6+3样式的表达式的计算器了.当然,你也可以将它拓展的更为强大. 我本意是想提供一个简单有趣的课程来讲解 语法分析 和 正规语法(编译原理内容).同时,介绍一下PlyPlus,这是一个我断断续续改进了好几年的语法解析 接口.作为这个课程的附加产物,我们最后会得到完全可替代eval()的一个安全的四则运算器. 如果你想在自家的电脑上试试

  • Python 利用scrapy爬虫通过短短50行代码下载整站短视频

    近日,有朋友向我求助一件小事儿,他在一个短视频app上看到一个好玩儿的段子,想下载下来,可死活找不到下载的方法.这忙我得帮,少不得就抓包分析了一下这个app,找到了视频的下载链接,帮他解决了这个小问题. 因为这个事儿,勾起了我另一个念头,这不最近一直想把python爬虫方面的知识梳理梳理吗,干脆借机行事,正凑着短视频火热的势头,做一个短视频的爬虫好了,中间用到什么知识就理一理. 我喜欢把事情说得很直白,如果恰好有初入门的朋友想了解爬虫的技术,可以将就看看,或许对你的认识会有提升.如果有高手路过,

  • 50行代码实现贪吃蛇(具体思路及代码)

    最近一直在准备用来面试的几个小demo,为了能展现自己,所以都是亲自设计并实现的,其中一个就是在50行代码内来实现一个贪吃蛇,为了说明鄙人自己练习编程的一种方式--把代码写短,为了理解语言细节. 复制代码 代码如下: <SPAN style="FONT-SIZE: 14px">import sys, pygame from pygame.locals import * from random import randrange up =lambda x:(x[0]-1,x[1

  • python不到50行代码完成了多张excel合并的实现示例

    一 前言 公司同事最近在做excel相关的工作:今天来求助知识追寻者合并多个excel为一个一个工作本,原本是java操作poi太蛋疼了,笨重不堪,内存消耗严重,知识追寻者使用python不到40行代码完成了60多张excel工作本合并为一张:python真香 牛皮吹完了,如果看过知识追寻者系列文章的读者肯定知道之前知识追寻者发过一篇 python专题使用openpyxl操作excel:本篇使用的不是openpyx库,使用的使是xlrd,xlwt库,虽然这两库功能没法根openpyx相比,但可以

  • 50行代码实现Webpack组件使用次数统计

    背景 最近有个领导想让我们搭组件库,然后我就想知道目前项目中使用的三方组件库哪些组件使用频率最高.本来想去咨询小伙伴,但是小伙伴太忙了,只能自己弄了.我就想能不能通过 webpack 来实现我的想法 效果 我们是用的 @material-ui,下面是组件使用情况 实现 我们知道 loader 的 source是文件的静态字符串如下图 最快的方案通过字符串统计用正则的方式一把梭,但是这样会有问题就是如果注释部分有的话也会被统计进去就不准确,所以我们可以通过 AST 的方式来实现,关于 ast 的概

  • 100行代码实现一个vue分页组功能

    今天用vue来实现一个分页组件,总体来说,vue实现比较简单,样式部分模仿了elementUI.所有代码的源码可以再github上下载的到:下载地址 先来看一下实现效果: 点击查看效果 整体思路 我们先看一下使用到的文件的目录: 我们在 pageComponentsTest.vue 页面引入了 pageComponent.vue 分页组件.整体思路是通过 props 来达到组件的灵活通用的效果,整体语法是使用vue的VM语法. pageComponent.vue实现 首先实现一个分页,需要知道数

  • Python+tkinter使用80行代码实现一个计算器实例

    本文主要探索的是使用Python+tkinter编程实现一个简单的计算器代码示例,具体如下. 闲话不说,直奔主题.建议大家跟着敲一遍代码,体会一下代码复用.字符串方法的运用和动态创建组件的妙处,然后在这个框架的基础上进行补充和发挥. 选择任何一款Python开发环境,创建一个程序文件,命名为tkinter_Calculator.pyw,然后编写下面的代码: 1)导入标准库re和tkinter,创建并简单设置应用主程序,在窗口顶部放置一个只读的文本框用来显示信息. 2)编写计算器上各种按钮的通用处

  • 80行代码写一个Webpack插件并发布到npm

    1. 前言 最近在学习 Webpack 相关的原理,以前只知道 Webpack 的配置方法,但并不知道其内部流程,经过一轮的学习,感觉获益良多,为了巩固学习的内容,我决定尝试自己动手写一个插件. 这个插件实现的功能比较简单: 默认清除 js 代码中的 console.log 的打印输出: 可通过传入配置,实现移除 console 的其它方法,如 console.warn.console.error 等: 2. Webpack 的构建流程以及 plugin 的原理 2.1 Webpack 构建流程

  • 手把手带你用React撸一个日程组件

    目录 业务背景 使用技术 技术难点 设计思路

随机推荐