iOS组件化-通信

216 阅读3分钟

组件间的通信有很多实现方式,常用的方式有:

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

URL Scheme路由

组件间的通信使用URL的方式,通过一个URL来实现跳转或者传值。这个方案的优点就是简单,缺点是需要维护一个字符串表,而且字符串的错误无法在编译期发现,只能等到运行时才能发现,无形中增加了很多风险。

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

其实现思路是:

  • App启动时实例化各组件模块,然后这些组件向ModuleManager注册Url,有些时候不需要实例化,使用class注册
  • 当组件A需要调用组件B时,向ModuleManager传递URL,参数跟随URL以GET方式传递,类似openURL。然后由ModuleManager负责调度组件B,最后完成任务。

我们使用《结构》一文中的结构示例图来做一个演示:

在底层有一个TYRouterManager组件,这个组件主要是用于通信的模块。

我们让这个组件依赖MGJRouter,当然,在实际开发中,我们肯定会在这个组件中做一些其他操作,比如:URL解析、参数包装、参数拦截等等功能。但在这里我们就简单示范一下MGJRouter的功能,就不做其他的封装了。 我们让Common层的TYTestPodA依赖于TYRouterManager,在TYTestPodA的podspec中添加依赖。

s.dependency 'TYRouterManager', '0.1.0'

MGJRouter的方式修改我们的代码,在代码中添加注册URL的方法

@implementation TYTestPodA
+(void)registerPodA {
    [MGJRouter registerURLPattern:@"ty://showPodAInfo/" toHandler:^(NSDictionary *routerParameters) {        TYTestPodA * a = [[TYTestPodA alloc] init];
        NSString *ainfo = [a showPodAInfo];

        NSDictionary * parameterDic = routerParameters[MGJRouterParameterUserInfo];
        if([parameterDic.allKeys containsObject:@"user"]) {
            NSLog(@"外部带入的参数打印:%@", parameterDic[@"user"]);
        }

        void (^completion)(id) = routerParameters[MGJRouterParameterCompletion];
        if(completion) {
            completion(@"这是PodA内部传出去的内容");
        }

    }];
}

- (NSString *)showPodAInfo {
    return @"hello, this is TYTestPodA";
}
@end

然后我们在提供给外部使用具体的方法的时候只需要暴露给外部一个注册方法即可,当然registerURLPattern方法可以直接放在load方法中,这样连给外部暴露registerPodA的过程都省了,但是不推荐这样做,因为registerURLPattern会注册很多,这样会拖慢app的启动速度。

@interface TYTestPodA : NSObject
+(void)registerPodA;
@end

更新version

s.version          = '0.1.1'

最后推送到我们的私有repo中。

最上层的Project只需要在合适的位置调用的注册方法,这样就可以使用具体的TYTestPodA中注册的方法了。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    // 在合适的位置调用注册方法
    [TYTestPodA registerPodA];
    return YES;
}
- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.

    // 调用showPodAInfo
    //外部带入的参数打印:pod_A
    //这是PodA内部传出去的内容
    [MGJRouter openURL:@"ty://showPodAInfo/" withUserInfo:@{
        @"user":@"pod_A",
    }completion:^(id result) {
        NSLog(@"%@", result);
    }];
}

Target - Action

上面我们说了URL Scheme这种通信方式,需要先注册才能使用,而Target-Action方案,只存在组件依赖Mediator中介的这一层依赖关系,并不需要注册即可实现组件间的调用。

Untitled.png

我们仍然使用《结构》一文中的结构示例图来做一个演示:

首先,根据上图的结构,我们Common层的TYTestPodA 就是一个Module,我们需要在TYTestPodA 中新建一个Target来存放Action。

@interface TYTargetPodA : NSObject
-(void)actionPodAWithParams:(NSDictionary *) aParams;
@end
#import "TYTargetPodA.h"
#import "TYTestPodA.h"

@implementation TYTargetPodA
-(void)actionPodAWithParams:(NSDictionary *) aParams {
    TYTestPodA * podA = [[TYTestPodA alloc] init];
    if(aParams && [aParams.allKeys containsObject:@"infoA"]) {
        NSLog(@"%@,%@", [podA showPodAInfo], aParams[@"infoA"]);
    } else {
        NSLog(@"%@", [podA showPodAInfo]);
    }
    
}
@end

然后,我们在底层的TYRouterManager 中新建一个Category,来连接我们的TYTargetPodA

@interface CTMediator (poda)
- (id)showPodAInfoByTargetActionWithParams:(NSDictionary *) aParams;
@end
#import "CTMediator+poda.h"

NSString * const kCTMediatorTarget = @"TYTargetPodA";
NSString * const kCTMediatorAction = @"actionPodAWithParams";

@implementation CTMediator (poda)
- (id)showPodAInfoByTargetActionWithParams:(NSDictionary *) aParams {
    return [self performTarget:kCTMediatorTarget action:kCTMediatorAction params:aParams];
}

@end

这样,我们按照上图CTMediator的结构就完成了一个简单的Target-Action,最后在我们的Project中简单的测试即可。

CTMediator * mediator = [CTMediator sharedInstance];
[mediator showPodAInfoByTargetActionWithParams:@{
    @"infoA": @"mediator-poda",
}];

当然,在实际开发中我们仍然是需要对CTMediator做一个封装,使其更易用。

Protocol - Class这种方式我在实际开发过程中并没有使用过,有兴趣的朋友可以自己研究一下。