0


iOS 组件化的三种方案

组件化

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

1、常⽤的三种方案

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

1.1、 URL Scheme路由

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

URL Scheme路由示例

  1. //MTMediator.h --- start
  2. typedef void(^MTMediatorProcessBlock)(NSDictionary *params);
  3. + (void)registerScheme:(NSString *)scheme processBlock:(MTMediatorProcessBlock)processBlock;
  4. + (void)openUrl:(NSString *)url params:(NSDictionary *)params;
  5. //MTMediator.h --- end
  6. //MTMediator.m --- start
  7. + (NSMutableDictionary *)mediatorCache{
  8. static NSMutableDictionary *cacheScheme;
  9. static dispatch_once_t onceToken;
  10. dispatch_once(&onceToken, ^{
  11. cacheScheme = @{}.mutableCopy;
  12. });
  13. return cacheScheme;
  14. }
  15. + (void)registerScheme:(NSString *)scheme processBlock:(MTMediatorProcessBlock)processBlock{
  16. if (scheme.length > 0 && processBlock) {
  17. [[[self class] mediatorCache] setObject:processBlock forKey:scheme];
  18. }
  19. }
  20. + (void)openUrl:(NSString *)url params:(NSDictionary *)params{
  21. MTMediatorProcessBlock block = [[[self class] mediatorCache] objectForKey:url];
  22. if (block) {
  23. block(params);
  24. }
  25. }
  26. //MTMediator.m --- end
  27. //注册 --- start
  28. + (void)load {
  29. [MTMediator registerScheme:@"detail://" processBlock:^(NSDictionary * _Nonnull params) {
  30. NSString *url = (NSString *)[params objectForKey:@"url"];
  31. UINavigationController *navigationController = (UINavigationController *)[params objectForKey:@"controller"];
  32. MTDetailViewController *controller = [[MTDetailViewController alloc] initWithUrlString:url];
  33. // controller.title = [NSString stringWithFormat:@"%@", @(indexPath.row)];
  34. [navigationController pushViewController:controller animated:YES];
  35. }];
  36. }
  37. //注册 --- end
  38. //调用 --- start
  39. //URL Scheme
  40. [MTMediator openUrl:@"detail://" params:@{@"url":item.articleUrl,@"controller":self.navigationController}];
  41. //调用 --- end
  42. 复制代码
  • 参考了系统URL Scheme机制
  • 参数传递通过dictionary,对调用者不透明

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

优点:实现简单

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

MGJRouter

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

实现原理:

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

URL 路由的优点

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

URl 路由的缺点

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

1.2、Target - Action

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

Target - Action示例

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

CTMediator

原理是通过oc的runtime、category特性动态获取模块,例如通过

  1. NSClassFromString

获取类并创建实例,通过

  1. performSelector + NSInvocation

动态调用方法。

实现原理:

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

CTMediator使用

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

模块间的关系:

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

优点

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

缺点

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

1.2、Protocol - Class

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

Protocol - Class示例

  1. //具体的Protocol
  2. //MTMediator.h --- start
  3. @protocol MTDetailViewControllerProtocol <NSObject>
  4. + (__kindof UIViewController *)detailViewControllerWithUrl:(NSString *)detailUrl;
  5. @end
  6. @interface MTMediator : NSObject
  7. + (void)registerProtol:(Protocol *)protocol class:(Class)cls;
  8. + (Class)classForProtocol:(Protocol *)protocol;
  9. @end
  10. //MTMediator.h --- end
  11. //MTMediator.m --- start
  12. + (void)registerProtol:(Protocol *)protocol class:(Class)cls{
  13. if (protocol && cls) {
  14. [[[self class] mediatorCache] setObject:cls forKey:NSStringFromProtocol(protocol)];
  15. }
  16. }
  17. + (Class)classForProtocol:(Protocol *)protocol{
  18. return [[[self class] mediatorCache] objectForKey:NSStringFromProtocol(protocol)];
  19. }
  20. //MTMediator.m --- end
  21. //被调用
  22. //MTDetailViewController.h --- start
  23. @protocol MTDetailViewControllerProtocol;
  24. @interface MTDetailViewController : UIViewController<MTDetailViewControllerProtocol>
  25. @end
  26. //MTDetailViewController.h --- end
  27. //MTDetailViewController.m --- start
  28. + (void)load {
  29. [MTMediator registerProtol: @protocol(MTDetailViewControllerProtocol) class:[self class]];
  30. }
  31. #pragma mark - MTDetailViewControllerProtocol
  32. + ( __kindof UIViewController *)detailViewControllerWithUrl:(NSString *)detailUrl{
  33. return [[MTDetailViewController alloc]initWithUrlString:detailUrl];
  34. }
  35. //MTDetailViewController.m --- end
  36. //调用
  37. Class cls = [MTMediator classForProtocol: @protocol(MTDetailViewControllerProtocol)];
  38. if ([cls respondsToSelector: @selector(detailViewControllerWithUrl:)]) {
  39. [self.navigationController pushViewController:[cls detailViewControllerWithUrl:item.articleUrl] animated:YES];
  40. }
  41. 复制代码
  • 被调用者先在中间件注册Protocol和Class对应关系,对外只暴漏Protocol

BeeHive

protocol比较典型的三方框架就是阿里的BeeHive。

  1. BeeHive

借鉴了Spring Service、Apache DSO的架构理念,

  1. 采用AOP+扩展App生命周期API

形式,将

  1. 业务功能

  1. 基础功能

模块以模块方式以解决大型应用中的复杂问题,并让

  1. 模块之间以Service形式调用

,将复杂问题切分,以AOP方式模块化服务。

BeeHive 核心思想

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

示例如下:

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

优点

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

缺点

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

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

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


​​​​​​​

标签: 前端 ios objective-c

本文转载自: https://blog.csdn.net/yezuiqingxin/article/details/126018623
版权归原作者 迷曳 所有, 如有侵权,请联系我们删除。

“iOS 组件化的三种方案”的评论:

还没有评论