一、蘑菇街url-block方案

  这是蘑菇街中应用的一种页面间调用的方式,通过在启动时注册组件提供的服务,把调用组件使用的url和组件提供的服务block对应起来,保存到内存中。在使用组件的服务时,通过url找到对应的block,然后获取服务。

  具体实现代码如下:

//Mediator.m 中间件
@implementation Mediator
typedef void (^componentBlock) (id param);
@property (nonatomic, storng) NSMutableDictionary *cache
- (void)registerURLPattern:(NSString *)urlPattern toHandler:(componentBlock)blk {
  [cache setObject:blk forKey:urlPattern];
}
- (void)openURL:(NSString *)url withParam:(id)param {
  componentBlock blk = [cache objectForKey:url];
  if (blk) blk(param);
}
@end

1.先在Mediator中注册url-block

#import "Mediator.h"
#import "WRBookDetailViewController.h"
+ (void)initComponent {
  [[Mediator sharedInstance] registerURLPattern:@"weread://bookDetail" toHandler:^(NSDictionary *param) {
    WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]];
    [[UIApplication sharedApplication].keyWindow.rootViewController.navigationController pushViewController:detailVC animated:YES];
  }];
}

2.通过url在中间件中就能找到对应的block,并执行跳转

#import "Mediator.h"
+ (void)gotoDetail:(NSString *)bookId {
  [[Mediator sharedInstance] openURL:@"weread://bookDetail" withParam:@{@"bookId": bookId}];
}

二、 蘑菇街protocol-class方案

  针对url-block方案无法传递非正规参数,新增一种protocol-class方案

//ProtocolMediator.m 中间件
@implementation ProtocolMediator
@property (nonatomic, storng) NSMutableDictionary *protocolCache
- (void)registerProtocol:(Protocol *)proto forClass:(Class)cls {
  NSMutableDictionary *protocolCache;
  [protocolCache setObject:cls forKey:NSStringFromProtocol(proto)];
} - (Class)classForProtocol:(Protocol *)proto {
  return protocolCache[NSStringFromProtocol(proto)];
}
@end

每一个组件都有一个公共Protocol文件,定义了每一个组件对外提供的接口

//ComponentProtocol.h
@protocol BookDetailComponentProtocol <NSObject>
- (UIViewController *)bookDetailController:(NSString *)bookId;
- (UIImage *)coverImageWithBookId:(NSString *)bookId;
@end @protocol ReviewComponentProtocol <NSObject>
- (UIViewController *)ReviewController:(NSString *)bookId;
@end //BookDetailComponent 组件
#import "ProtocolMediator.h"
#import "ComponentProtocol.h"
#import "WRBookDetailViewController.h"
+ (void)initComponent{
  [[ProtocolMediator sharedInstance] registerProtocol:@protocol(BookDetailComponentProtocol) forClass:[self class];
} - (UIViewController *)bookDetailController:(NSString *)bookId {
  WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]];
  return detailVC;
} - (UIImage *)coverImageWithBookId:(NSString *)bookId {
  ….
}

最后调用者通过 protocol 从 ProtocolMediator 拿到提供这些方法的 Class,再进行调用:

//WRReadingViewController.m 调用者
//ReadingViewController.m
#import "ProtocolMediator.h"
#import "ComponentProtocol.h"
+ (void)gotoDetail:(NSString *)bookId {
  Class cls = [[ProtocolMediator sharedInstance] classForProtocol:BookDetailComponentProtocol];
  id bookDetailComponent = [[cls alloc] init];
  UIViewController *vc = [bookDetailComponent bookDetailController:bookId];
  [self.navigationController pushViewController:vc animated:YES];
}

三、target-action方案

  1.先设置一个中间件公共类

@interface CTMediator : NSObject

+ (instancetype)sharedInstance;

// 远程App调用入口
- (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion;
// 本地组件调用入口
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;
- (void)releaseCachedTargetWithTargetName:(NSString *)targetName; @end //
// CTMediator.m
// CTMediator
//
// Created by casa on 16/3/13.
// Copyright © 2016年 casa. All rights reserved.
// #import "CTMediator.h" @interface CTMediator () @property (nonatomic, strong) NSMutableDictionary *cachedTarget; @end @implementation CTMediator #pragma mark - public methods
+ (instancetype)sharedInstance
{
static CTMediator *mediator;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mediator = [[CTMediator alloc] init];
});
return mediator;
} /*
scheme://[target]/[action]?[params] url sample:
aaa://targetA/actionB?id=1234
*/ - (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion
{
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
NSString *urlString = [url query];
for (NSString *param in [urlString componentsSeparatedByString:@"&"]) {
NSArray *elts = [param componentsSeparatedByString:@"="];
if([elts count] < 2) continue;
[params setObject:[elts lastObject] forKey:[elts firstObject]];
} // 这里这么写主要是出于安全考虑,防止黑客通过远程方式调用本地模块。这里的做法足以应对绝大多数场景,如果要求更加严苛,也可以做更加复杂的安全逻辑。
NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
if ([actionName hasPrefix:@"native"]) {
return @(NO);
} // 这个demo针对URL的路由处理非常简单,就只是取对应的target名字和method名字,但这已经足以应对绝大部份需求。如果需要拓展,可以在这个方法调用之前加入完整的路由逻辑
id result = [self performTarget:url.host action:actionName params:params shouldCacheTarget:NO];
if (completion) {
if (result) {
completion(@{@"result":result});
} else {
completion(nil);
}
}
return result;
} - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{ NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName]; id target = self.cachedTarget[targetClassString];
if (target == nil) {
Class targetClass = NSClassFromString(targetClassString);
target = [[targetClass alloc] init];
} SEL action = NSSelectorFromString(actionString); if (target == nil) {
// 这里是处理无响应请求的地方之一,这个demo做得比较简单,如果没有可以响应的target,就直接return了。实际开发过程中是可以事先给一个固定的target专门用于在这个时候顶上,然后处理这种请求的
return nil;
} if (shouldCacheTarget) {
self.cachedTarget[targetClassString] = target;
} if ([target respondsToSelector:action]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
} else {
// 这里是处理无响应请求的地方,如果无响应,则尝试调用对应target的notFound方法统一处理
SEL action = NSSelectorFromString(@"notFound:");
if ([target respondsToSelector:action]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
} else {
// 这里也是处理无响应请求的地方,在notFound都没有的时候,这个demo是直接return了。实际开发过程中,可以用前面提到的固定的target顶上的。
[self.cachedTarget removeObjectForKey:targetClassString];
return nil;
}
}
} - (void)releaseCachedTargetWithTargetName:(NSString *)targetName
{
NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
[self.cachedTarget removeObjectForKey:targetClassString];
} #pragma mark - getters and setters
- (NSMutableDictionary *)cachedTarget
{
if (_cachedTarget == nil) {
_cachedTarget = [[NSMutableDictionary alloc] init];
}
return _cachedTarget;
} @end

2.每个组件创建一个Category,在其中实现当前组件的跳转实现

#import <CTMediator/CTMediator.h>
#import <UIKit/UIKit.h> @interface CTMediator (TAConfirmOrder) - (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName ConfirmComplete:(dispatch_block_t)confirmComplete; @end #import "CTMediator+TAConfirmOrder.h" @implementation CTMediator (TAConfirmOrder) - (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName ConfirmComplete:(dispatch_block_t)confirmComplete
{
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
params[@"goodsId"] = goodsId;
params[@"goodsName"] = goodsName;
params[@"completeBlock"] = confirmComplete;
return [self performTarget:@"TAConfirmOrder" action:@"ConfirmOrderViewController" params:params shouldCacheTarget:NO];
} @end

3.通过中间件调用

UIViewController *confirmOrderVC = [[CTMediator sharedInstance] confirmOrderViewControllerWithGoodsId:self.goodsId  goodsName:self.goodsName  ConfirmComplete:^{

//...

}];

iOS组件化方案的更多相关文章

  1. iOS 组件化方案

    概述 近一年iOS业界讨论组件化方案甚多,大体来说有3种. Protocol注册方案 URL注册方案 Target-Action runtime调用方案 URL注册方案据我了解很多大公司都在采用,蘑菇 ...

  2. iOS应用架构谈 组件化方案

    转载: iOS应用架构谈 组件化方案 简述 前几天的一个晚上在infoQ的微信群里,来自蘑菇街的Limboy做了一个分享,讲了蘑菇街的组件化之路.我不认为这条组件化之路蘑菇街走对了.分享后我私聊了Li ...

  3. iOS组件化实现方案

    作者原文iOS组件化 - 路由架构从0到1实战  合伙呀 1.CTMediator作为路由中间件 2.基础UI组件以pod形式引入,并且能够独立运行调试 3.基础工具组件以pod形式引入,并且能够独立 ...

  4. iOS组件化思路 <转>

    随着应用需求逐步迭代,应用的代码体积将会越来越大,为了更好的管理应用工程,我们开始借助CocoaPods版本管理工具对原有应用工程进行拆分.但是仅仅完成代码拆分还不足以解决业务之间的代码耦合,为了更好 ...

  5. iOS组件化思路-大神博客研读和思考

    一.大神博客研读 随着应用需求逐步迭代,应用的代码体积将会越来越大,为了更好的管理应用工程,我们开始借助CocoaPods版本管理工具对原有应用工程进行拆分.但是仅仅完成代码拆分还不足以解决业务之间的 ...

  6. iOS 组件化 —— 路由设计思路分析

    原文 前言 随着用户的需求越来越多,对App的用户体验也变的要求越来越高.为了更好的应对各种需求,开发人员从软件工程的角度,将App架构由原来简单的MVC变成MVVM,VIPER等复杂架构.更换适合业 ...

  7. iOS 组件化的几篇文章

    随着工程的成长,开发人员的增多,合理的模块划分及低耦合的重要性显得愈发重要.最近在思考这方面的问题,也读了不少通过组件化解耦的文章,这里记录一下. 前 5 篇文章有些关联,建议阅读顺序,1.3.2.4 ...

  8. iOS 组件化路由框架 WisdomRouterKit 的应用

    [前言] 大家好,写作是为了和读者沟通交流,欢迎各位开发者一起了解 WisdomRouterKit SDK 的功能. 关于 iOS 组件化路由方案框架: WisdomRouterKit 的功能介绍,之 ...

  9. Vue.js:轻量高效的前端组件化方案

    转发一篇尤老师对vue.js的介绍,了解vue.js的来龙去脉.不过现在已经是2.0了,也有添加一些新的东西,当然有些东西也改了. Vue.js:轻量高效的前端组件化方案 Vue.js 是我在2014 ...

随机推荐

  1. Java 时间工具类

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  1.Calendar 转化 String  ...

  2. Weblogic-unable to get file lock, will retry …问题解决

    weblogic部署应用出现如下报错: <2017-8-15 下午05时08分44秒 CST> <Info> <Management> <BEA-141281 ...

  3. Dockerfile 部署应用执行脚本文件

    FROM centos6.6:0.0.1 MAINTAINER syberos:wangmo RUN mv /etc/yum.repos.d/ /etc/yum.repos.d_bak/ && ...

  4. CEF源码编译

    CEF的构造说明:https://bitbucket.org/chromiumembedded/cef/wiki/BranchesAndBuilding chromium的源码地址:https://c ...

  5. 在CentOS上安装Java开发环境:使用yum安装jdk

    请参考百度经验:http://jingyan.baidu.com/article/4853e1e51d0c101909f72607.html 如果您阅读过此文章有所收获,请为我顶一个,如果文章中有错误 ...

  6. JAVA中数值的表示

    1.Java中用补码形式表示 2.第一位正负位,1表示负,0表示正. 3.原码:一个数的二进制表示.                  3的原码00000011   -3的 原码 10000011 4 ...

  7. ps命令,性能监控,grep命令

    Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信 ...

  8. Java-API:java.util.list

    ylbtech-Java-API:java.util.list 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部 1. https://docs.oracle.co ...

  9. 2016.1.23 通过cmd在程序中执行sql脚本

    System.Diagnostics.Process pro = new System.Diagnostics.Process(); pro.StartInfo.FileName = "cm ...

  10. 谈谈开发文本转URL小工具的思路

    URL提供了一种定位互联网上任意资源的手段,由于采用HTTP协议的URL能在互联网上自由传播和使用,所以能大行其道.在软件开发.测试甚至部署的环节,URL几乎可以说无处不再,其中用来定位文本的URL数 ...