组件化

本文主要介绍iOS组件化的三种方案

1、常⽤的三种方案

  • URL Scheme
  • Target - Action
  • Protocol - Class 匹配

1.1、 URL Scheme路由

  • 使 URL 处理本地的跳转
  • 通过中间层进⾏注册 & 调⽤ (load方法里把被调用者注册到中间层)
  • 注册表⽆需使用反射
  • 非懒加载 / 注册表的维护 / 参数

URL Scheme路由示例

//MTMediator.h --- start
typedef void(^MTMediatorProcessBlock)(NSDictionary *params);+ (void)registerScheme:(NSString *)scheme processBlock:(MTMediatorProcessBlock)processBlock;+ (void)openUrl:(NSString *)url params:(NSDictionary *)params;
//MTMediator.h --- end//MTMediator.m --- start
+ (NSMutableDictionary *)mediatorCache{static NSMutableDictionary *cacheScheme;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{cacheScheme = @{}.mutableCopy;});return cacheScheme;
}+ (void)registerScheme:(NSString *)scheme processBlock:(MTMediatorProcessBlock)processBlock{if (scheme.length > 0 && processBlock) {[[[self class] mediatorCache] setObject:processBlock forKey:scheme];}
}+ (void)openUrl:(NSString *)url params:(NSDictionary *)params{MTMediatorProcessBlock block = [[[self class] mediatorCache] objectForKey:url];if (block) {block(params);}
}
//MTMediator.m --- end//注册 --- start
+ (void)load {[MTMediator registerScheme:@"detail://" processBlock:^(NSDictionary * _Nonnull params) {NSString *url = (NSString *)[params objectForKey:@"url"];UINavigationController *navigationController = (UINavigationController *)[params objectForKey:@"controller"];MTDetailViewController *controller = [[MTDetailViewController alloc] initWithUrlString:url];
//        controller.title = [NSString stringWithFormat:@"%@", @(indexPath.row)];[navigationController pushViewController:controller animated:YES];}];
}
//注册 --- end//调用 --- start
//URL Scheme
[MTMediator openUrl:@"detail://" params:@{@"url":item.articleUrl,@"controller":self.navigationController}];
//调用 --- end
复制代码
  • 参考了系统URL Scheme机制
  • 参数传递通过dictionary,对调用者不透明

目前iOS上大部分路由工具都是基于URL 进行匹配的,或者命名约定,通过runtime方法进行动态调用

优点:实现简单

缺点:需要维护字符串表,依赖于命名约定,无法在编译时暴露出所有问题,需要在运行时才能发现错误。

MGJRouter

URL路由方式主要是以蘑菇街为代表的的MGJRouter

实现原理:

  • App启动时实例化各组件模块,然后这些组件向ModuleManager注册Url,有些时候不需要实例化,使用class注册
  • 当组件A需要调用组件B时,向ModuleManager传递URL,参数跟随URL以GET方式传递,类似openURL。然后由ModuleManager负责调度组件B,最后完成任务。
// 1、注册某个URL
MGJRouter.registerURLPattern("app://home") { (info) inprint("info: (info)")
}//2、调用路由
MGJRouter.openURL("app://home")
复制代码

URL 路由的优点

  • 极高的动态性,适合经常开展运营活动的app
  • 方便地统一管理多平台的路由规则
  • 易于适配URL Scheme,可以下发

URl 路由的缺点

  • 传参方式有限,并且无法利用编译器进行参数类型检查,因此所有的参数都是通过字符串转换而来
  • 只适用于界面模块,不适用于通用模块
  • 参数的格式不明确,是个灵活的 dictionary,也需要有个地方可以查参数格式。
  • 不支持storyboard
  • 依赖于字符串硬编码,难以管理,蘑菇街做了个后台专门管理。
  • 无法保证所使用的的模块一定存在
  • 解耦能力有限,url 的”注册”、”实现”、”使用”必须用相同的字符规则,一旦任何一方做出修改都会导致其他方的代码失效,并且重构难度大

1.2、Target - Action

  • 抽离业务逻辑
  • 通过中间层进行调⽤
  • 中间层使⽤ runtime 反射
  • 中间层代码优化

Target - Action示例

//MTMediator.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface MTMediator : NSObject//target action
+ ( __kindof UIViewController *)detailViewControllerWithUrl:(NSString *)detailUrl;@endNS_ASSUME_NONNULL_END//MTMediator.m
#import "MTMediator.h"@implementation MTMediator+ ( __kindof UIViewController *)detailViewControllerWithUrl:(NSString *)detailUrl{Class detailVC = NSClassFromString(@"MTDetailViewController");UIViewController *controller = [[detailVC alloc] performSelector:NSSelectorFromString(@"initWithUrlString:") withObject:detailUrl];return controller;
}@end//调用
//Target - ActionUIViewController *vc = [MTMediator detailViewControllerWithUrl:item.articleUrl];vc.title = @"详情啊";[self.navigationController pushViewController:vc animated:YES];复制代码
  • 硬编码方式(直接调用,不利于维护和扩展)
  • perform 最多能传递2个参数,可以传入字典避免参数过多
  • initWithUrlString:方法必须实现 否则找不到sel崩溃
  • 业务逻辑柔合在Mediator中,可以各个模块写各自的MTMediator扩展

CTMediator

原理是通过oc的runtime、category特性动态获取模块,例如通过NSClassFromString获取类并创建实例,通过performSelector + NSInvocation动态调用方法。

实现原理:

  • 1、利用分类为路由添加新接口,在接口中通过字符串获取对应的类
  • 2、通过runtime创建实例,动态调用实例的方法

CTMediator使用

//******* 1、分类定义新接口
extension CTMediator{@objc func A_showHome()->UIViewController?{//在swift中使用时,需要传入对应项目的target名称,否则会找不到视图控制器let params = [kCTMediatorParamsKeySwiftTargetModuleName: "CJLBase_Example"]//CTMediator提供的performTarget:action:params:shouldCacheTarget:方法 通过传入name,找到对应的targer和actionif let vc = self.performTarget("A", action: "Extension_HomeViewController", params: params, shouldCacheTarget: false) as? UIViewController{return vc}return nil}
}//******* 2、模块提供者提供target-action的调用方式(对外需要加上public关键字)
class Target_A: NSObject {@objc func Action_Extension_HomeViewController(_ params: [String: Any])->UIViewController{let home = HomeViewController()return home}}//******* 3、使用
if let vc = CTMediator.sharedInstance().A_showHome() {self.navigationController?.pushViewController(vc, animated: true)}
复制代码

模块间的关系:

模块A——Mediator——target——模块B

优点

  • 1、利用接口调用,实现了参数传递时的类型安全
  • 2、直接使用模块的protocol接口,无需再重复封装

缺点

  • 1、用框架来创建所有对象,创建方式不同,即不支持外部传入参数
  • 2、用OC runtime创建对象,不支持swift
  • 3、只做了protocol 和 class 的匹配,不支持更复杂的创建方式 和依赖注入
  • 4、无法保证所使用的protocol 一定存在对应的模块,也无法直接判断某个protocol是否能用于获取模块

1.2、Protocol - Class

  • 增加 Protocol Wrapper层 (中间件先注册Protocol和Class对应关系,将protocol和对应的进行字典匹配
  • 中间件返回 Protocol 对应的 Class,然后动态创建实例
  • 解决硬编码的问题

Protocol - Class示例

//具体的Protocol
//MTMediator.h --- start
@protocol MTDetailViewControllerProtocol <NSObject>+ (__kindof UIViewController *)detailViewControllerWithUrl:(NSString *)detailUrl;@end@interface MTMediator : NSObject
+ (void)registerProtol:(Protocol *)protocol class:(Class)cls;
+ (Class)classForProtocol:(Protocol *)protocol;
@end
//MTMediator.h --- end//MTMediator.m --- start
+ (void)registerProtol:(Protocol *)protocol class:(Class)cls{if (protocol && cls) {[[[self class] mediatorCache] setObject:cls forKey:NSStringFromProtocol(protocol)];}
}+ (Class)classForProtocol:(Protocol *)protocol{return [[[self class] mediatorCache] objectForKey:NSStringFromProtocol(protocol)];
}
//MTMediator.m --- end//被调用
//MTDetailViewController.h --- start
@protocol MTDetailViewControllerProtocol;@interface MTDetailViewController : UIViewController<MTDetailViewControllerProtocol>
@end
//MTDetailViewController.h --- end//MTDetailViewController.m --- start
+ (void)load {[MTMediator registerProtol: @protocol(MTDetailViewControllerProtocol) class:[self class]];
}#pragma mark - MTDetailViewControllerProtocol
+ ( __kindof UIViewController *)detailViewControllerWithUrl:(NSString *)detailUrl{return [[MTDetailViewController alloc]initWithUrlString:detailUrl];
}
//MTDetailViewController.m --- end//调用
Class cls = [MTMediator classForProtocol: @protocol(MTDetailViewControllerProtocol)];
if ([cls respondsToSelector: @selector(detailViewControllerWithUrl:)]) {[self.navigationController pushViewController:[cls detailViewControllerWithUrl:item.articleUrl] animated:YES];
}复制代码
  • 被调用者先在中间件注册Protocol和Class对应关系,对外只暴漏Protocol

BeeHive

protocol比较典型的三方框架就是阿里的BeeHive。BeeHive借鉴了Spring Service、Apache DSO的架构理念,采用AOP+扩展App生命周期API形式,将业务功能基础功能模块以模块方式以解决大型应用中的复杂问题,并让模块之间以Service形式调用,将复杂问题切分,以AOP方式模块化服务。

BeeHive 核心思想

  • 1、各个模块间调用从直接调用对应模块,变成调用Service的形式,避免了直接依赖。
  • 2、App生命周期的分发,将耦合在AppDelegate中逻辑拆分,每个模块以微应用的形式独立存在。

示例如下:

//******** 1、注册
[[BeeHive shareInstance] registerService:@protocol(HomeServiceProtocol) service:[BHViewController class]];//******** 2、使用
#import "BHService.h"id< HomeServiceProtocol > homeVc = [[BeeHive shareInstance] createService:@protocol(HomeServiceProtocol)];
复制代码

优点

  • 1、利用接口调用,实现了参数传递时的类型安全
  • 2、直接使用模块的protocol接口,无需再重复封装

缺点

  • 1、用框架来创建所有对象,创建方式不同,即不支持外部传入参数
  • 2、用OC runtime创建对象,不支持swift
  • 3、只做了protocol 和 class 的匹配,不支持更复杂的创建方式 和依赖注入
  • 4、无法保证所使用的protocol 一定存在对应的模块,也无法直接判断某个protocol是否能用于获取模块

建议:URL Scheme - handler 配合 Protocol - Class 使用

附带:iOS组件化方案架构设计图


​​​​​​​

iOS 组件化的三种方案相关推荐

  1. 前端组件化的三种方案:

    1.预编译语言 在开发阶段,用更完善的第三方程序语言,生成html,css,js代码. 缺点:css有sass,less:js有CoffeeScript,TypeScripty:HTML呢? 优点:如 ...

  2. 基于embedded.mobileprovision描述文件查看iOS苹果证书到期时间三种方案

    在iOS开发中,embedded.mobileprovision描述文件是开发人员非常熟悉的,里面包含了证书信息,如调试设备UDID.Entitlements.AppIDName.DeveloperC ...

  3. 解读 iOS 组件化与路由的本质

    前言 虽然 iOS 组件化与路由的话题在业界谈了很久,但是貌似很多人都对其有所误解,甚至没搞明白"组件"."模块"."路由"."解 ...

  4. iOS-解读 iOS 组件化与路由的本质

    前言 虽然 iOS 组件化与路由的话题在业界谈了很久,但是貌似很多人都对其有所误解,甚至没搞明白"组件"."模块"."路由"."解 ...

  5. iOS 组件化与路由的本质

    前言 虽然 iOS 组件化与路由的话题在业界谈了很久,但是貌似很多人都对其有所误解,甚至没搞明白"组件"."模块"."路由"."解 ...

  6. iOS组件化开发从开始到完整总结

    一.组件化介绍 需求来源 随着项目规模不断扩大,业务模块增多,开发过程中会有多条产品线(多人或多小组开发不同的功能);如果用传统的开发模式,会导致代码臃肿,编译速度越来越慢,开发效率低下,代码维护成本 ...

  7. iOS 组件化,插件化,模块化设计思路分析

    iOS 组件化,插件化设计思路分析 前言 随着用户的需求越来越多,对App的用户体验也变的要求越来越高.为了更好的应对各种需求,开发人员从软件工程的角度,将App架构由原来简单的MVC变成MVVM,V ...

  8. React组件进阶--组件通讯介绍,组件的 props特点,组件通讯的三种方式子到父,父到子,兄弟到兄弟组件,Context,回顾练习

    1.组件通讯介绍 组件是独立且封闭的单元,默认情况下,只能使用组件自己的数据(state). 在组件化过程中,我们将一个完整的功能 拆分成多个组件,以更好的完成整个应用的功能. 而在这个过程中,多个组 ...

  9. sql优化之:数据库索引创建原则,or/in/union与索引优化,聚集索引/非聚集索引/联合索引/索引覆盖,MySQL冗余数据的三种方案,MySQL双主一致性架构优化(来源:架构师之路)

    一.一些常见的SQL实践 (1)负向条件查询不能使用索引 select * from order where status!=0 and stauts!=1 not in/not exists都不是好 ...

最新文章

  1. FLINK源代码调试方式
  2. R语言ggplot2可视化基本散点图(设置X轴使用对数坐标)、并把成对的数据点用线条(line)连接起来、自定义配置线条颜色(Connecting Paired Points with lines)
  3. Mac OS X10.11(OS X EI Capitan)安装程序下载
  4. fcn从头开始_从头开始有营销问题
  5. IOS开发之数据库FMDB的使用
  6. 企业短信平台发展趋势和展望——SUBMAIL赛邮
  7. np.digitize 用法详解
  8. 解读 Oracle 12c 自适应执行计划一例
  9. 解决Excel中使用VBA出现microsoft visual c++ runtime错误
  10. java架构模式与设计模式(三)--事件风暴
  11. 产品思维训练 | 滴滴顺风车新规:女性晚8点被“宵禁”,是保护还是歧视?
  12. 【JAVA】-- 多线程(线程让步yield、线程插队join)
  13. 前端发送请求的方法总结
  14. Springboot 小巧简便的限流器使用 RateLimiter
  15. js字母大小写进行转换
  16. js商场导航思路--ivx--canvas实现导航动画效果
  17. 根据生日判断是否大于18岁
  18. thinkvd将支持rmvb转换 (开发日志)
  19. office文档(ppt,excel,word,pdf等)在线预览
  20. 【区间DP】军训值日生

热门文章

  1. Web漏洞扫描工具汇总
  2. GDI+的DrawString多行、换行,自动换行
  3. 卡尔曼滤波中残差与新息的区别
  4. Java基础功---数组的使用和算法
  5. 支持分类页SEO设置的插件——基地
  6. 【OJ - 基础数学】求1+2+3+...+n(不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句)
  7. win7系统里设置打印服务器,详解Win7系统打印服务器的安装设置
  8. PrepareStatement用法(附源码解析)
  9. 程序员做的不开心怎么办?
  10. 适合邮件模板HTML outlook,163,qq邮箱,企业邮箱等