路由的基础知识
iOS 系统里面支持的URL Scheme方式,我们可以看出,对于一个资源的访问,苹果也是用URI的方式来访问的。
统一资源标识符(英语:Uniform Resource Identifier,或URI)是一个用于标识某一互联网资源名称的字符串。 该种标识允许用户对网络中(一般指万维网)的资源通过特定的协议进行交互操作。URI的最常见的形式是统一资源定位符(URL)。
路由开源的库:
http://www.cocoachina.com/ios/20170301/18811.html (参考如下的文章)
组件之间的通信
URL路由的方式
注册:MGJRouter
各个组件初始化时向 Mediator 注册对外提供的接口,Mediator 通过保存在内存的表去知道有哪些模块哪些接口,接口的形式是 URL->block。
这种方案思路就是:Mediator 不能直接去调用组件的方法,因为这样会产生依赖,那我就要通过其他方法去调用,也就是通过 字符串->方法 的映射去调用。runtime 接口的 className + selectorName -> IMP 是一种,注册表的 key -> block 是一种,而前一种是 OC 自带的特性,后一种需要内存维持一份注册表。
从数据结构来看
@interface MGJRouter ()
/**
* 保存了所有已注册的 URL
* 结构类似 @{@"beauty": @{@":id": {@"_", [block copy]}}}
*/
@property (nonatomic) NSMutableDictionary *routes;
@end
/**
* 打开此 URL,带上附加信息,同时当操作完成时,执行额外的代码
*
* @param URL 带 Scheme 的 URL,如 mgj://beauty/4
* @param userInfo 附加参数
* @param completion URL 处理完成后的 callback,完成的判定跟具体的业务相关
*/
+ (void)openURL:(NSString *)URL withUserInfo:(NSDictionary *)userInfo completion:(void (^)(id result))completion;
蘑菇街为了区分开页面间调用和组件间调用,于是想出了一种新的方法。用Protocol的方法来进行组件间的调用。
每个组件之间都有一个 Entry,这个 Entry,主要做了三件事:
注册这个组件关心的 URL
注册这个组件能够被调用的方法/属性
在 App 生命周期的不同阶段做不同的响应
页面间的openURL调用就是如下的样
每个组件间都会向MGJRouter注册,组件间相互调用或者是其他的App都可以通过openURL:方法打开一个界面或者调用一个组件。
在组件间的调用,蘑菇街采用了Protocol的方式。
[ModuleManager registerClass:ClassA forProtocol:ProtocolA] 的结果就是在 MM 内部维护的 dict 里新加了一个映射关系。
[ModuleManager classForProtocol:ProtocolA] 的返回结果就是之前在 MM 内部 dict 里 protocol 对应的 class,使用方不需要关心这个 class 是个什么东东,反正实现了 ProtocolA 协议,拿来用就行。
这里需要有一个公共的地方来容纳这些 public protocl,也就是图中的 PublicProtocl.h。
我猜测,大概实现可能是下面的样子:
然后这个是一个单例,在里面注册各个协议:
在ModuleProtocolManager中用一个字典保存每个注册的protocol。现在再来猜猜ModuleEntry的实现
然后每个模块内都有一个和暴露到外面的协议相连接的“接头”。
在它的实现中,需要引入3个外部文件,一个是ModuleProtocolManager,一个是DetailModuleEntryProtocol,最后一个是所在模块需要跳转或者调用的组件或者页面。
至此基于Protocol的方案就完成了。如果需要调用某个组件或者跳转某个页面,只要先从ModuleProtocolManager的字典里面根据对应的ModuleEntryProtocol找到对应的DetailModuleEntry,找到了DetailModuleEntry就是找到了组件或者页面的“入口”了。再把参数传进去即可。
这样就可以调用到组件或者界面了。
如果组件之间有相同的接口,那么还可以进一步的把这些接口都抽离出来。这些抽离出来的接口变成“元接口”,它们是可以足够支撑起整个组件一层的。