iOS之组件化 四

148 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情

URL路由

URL路由的方案相对简单,基于URL匹配,双方进行命名约定,使用Runtime方法进行动态调用

方案的代表框架:MGJRouter

最基本的使用

[MGJRouter registerURLPattern:@"mgj://foo/bar" toHandler:^(NSDictionary *routerParameters) {
    NSLog(@"routerParameterUserInfo:%@", routerParameters[MGJRouterParameterUserInfo]);
}];

[MGJRouter openURL:@"mgj://foo/bar"];

当匹配到URL后,routerParameters会自带几个key

extern NSString *const MGJRouterParameterURL;
extern NSString *const MGJRouterParameterCompletion;
extern NSString *const MGJRouterParameterUserInfo;

方案的思路:

  • App启动时实例化各组件模块,这些组件向ModuleManager注册URL。不需要实例化的组件,可使用Class进行注册
  • 组件A调用组件B时,向ModuleManager传递URL,可携带参数。使用封装的openURL方法,由ModuleManager负责组件B的调度

优点:

  • 动态性高,适合页面和参数自由度较高的电商类App
  • 多平台的路由规则可统一管理
  • 适用于URL Scheme

缺点:

  • 使用字符串传递,安全性和稳健性难以保证,被使用的模块不一定存在
  • 对于字符串的管理成本较高
  • 不支持storyboard
  • 一旦使用该方案,很难被替换。对于整个工程来说,重构难度加大
target-action

基于OCRuntimeCategory特性动态获取模块

  • 通过NSClassFromString获取类并创建实例
  • 通过performSelector + NSInvocation动态调用方法

方案的代表框架:CTMediator

最基本的使用

//1、创建CTMediator的分类,完成对外的接口
@implementation CTMediator (CTMediatorModuleAActions)

- (void)CTMediator_presentImage:(UIImage *)image
{
    [self performTarget:@"A"                 action:@"nativePresentImage"                 params:@{@"image":image}      shouldCacheTarget:NO];
}

@end

//2、添加Action,完成具体的业务
@implementation Target_A

- (id)Action_nativePresentImage:(NSDictionary *)params
{
    DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init];
    viewController.valueLabel.text = @"this is image";
    viewController.imageView.image = params[@"image"];
    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:viewController animated:YES completion:nil];
    return nil;
}

@end

//3、外部调用
[[CTMediator sharedInstance] CTMediator_presentImage:[UIImage imageNamed:@"image"]];

方案的思路:

  • 组件的核心CTMediator类,使用字符串按照指定规则,拿到真实的targetaction的名称
  • 通过方法签名,判断返回值类型,如果是非id类型,使用NSInvocation进行消息转发
    • 传入targetselectorargument
    • 使用getReturnValue方法将其返回
  • 否则,返回值id类型,直接使用performSelector进行方法调用

优点:

  • 利用分类将接口按业务拆分,去中心化
  • 框架核心代码短小精悍,实现方式轻量

缺点:

  • 每一个接口都需要中间方法,有些繁琐
  • 用字符串传递,被使用的模块不一定存在
  • 业务越复杂,创建的分类和target-action中间类就会越多

模块之间的通讯流程:

image-64.png