当我们组件化后,主项目与组件如何通讯成了主要问题。主项目要调用组件,想到的第一个办法就是把组件的
类
,方法
的访问权限
改为Public
。但是这么改,会导致耦合性
提高,如何解耦
呢,那就是在组件跟主项目中间再包一层,通过中间层去调用组件,那这个中间层就是CTMediator
了。
CTMediator
分为远程
调用跟本地
调用,如果没有其他APP,打开本地组件的需求,那只要用到本地
调用。
CTMediator
主要通过runtime
来调用组件模块,如果是OC
的调用只需要Target
,Action
便能调用指定模块下的方法。如果是Swift
的话,则需要多一个参数kCTMediatorParamsKeySwiftTargetModuleName
,因为OC
跟Swift
通过类名调用方法是不一样的。OC
的类名是Target
,Swift
的类名得指定模块Module.Target
。
这里大概说一下,
CTMediator
是如何通过Target
,Action
来调用对应的方法的。这里附带CTMediator
源码地址https://github.com/casatwy/CTMediator
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{
if (targetName == nil || actionName == nil) {
return nil;
}
/// 获取Swift专属的ModuleName
NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName];
// generate target
NSString *targetClassString = nil;
if (swiftModuleName.length > 0) {
// 如果是Swift,生成的targetName格式,Module.Target这种格式
targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];
} else {
// 如果是OC则直接用Target作为targetName
targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
}
// 从缓存拿target
NSObject *target = [self safeFetchCachedTarget:targetClassString];
// 如果没有缓存的target
if (target == nil) {
// 直接拿targetName来生成target
Class targetClass = NSClassFromString(targetClassString);
target = [[targetClass alloc] init];
}
// generate action
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
SEL action = NSSelectorFromString(actionString);
if (target == nil) {
// 这里是处理无响应请求的地方之一,这个demo做得比较简单,如果没有可以响应的target,就直接return了。实际开发过程中是可以事先给一个固定的target专门用于在这个时候顶上,然后处理这种请求的
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
return nil;
}
// 如果允许缓存,那么就保存Target,key为targetName
if (shouldCacheTarget) {
[self safeSetCachedTarget:target key:targetClassString];
}
if ([target respondsToSelector:action]) {
// 如果target能正常响应Action,就用签名的方式,调用对应target的方法
return [self safePerformAction:action target:target params:params];
} else {
// 这里是处理无响应请求的地方,如果无响应,则尝试调用对应target的notFound方法统一处理
SEL action = NSSelectorFromString(@"notFound:");
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
} else {
// 这里也是处理无响应请求的地方,在notFound都没有的时候,这个demo是直接return了。实际开发过程中,可以用前面提到的固定的target顶上的。
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
@synchronized (self) {
[self.cachedTarget removeObjectForKey:targetClassString];
}
return nil;
}
}
}
这里是调用CTMediator
的入口,也算是最主要的代码了,通过拿到Target
,Action
再利用方法签名的方式,调用对应Target
里面的方法(Action)
,以达到主工程调用组件的目的。具体怎么拿Target
,Action
,代码已经给上注释了。要注意的是如果是Swift的话,会拿ModuleName
,所拼接的Target也是跟OC不一样的。
[self safePerformAction:action target:target params:params]
这个方法里面的代码就不贴出来了,可以下载源码自己进去看,这里就是用方法签名来调用对应Target的方法(Action)
这个库的代码不多,也不难,主要是思路。怎么用的话,可以下载源码
,里面有demo
,Swift
,OC
都有。组件化最基础的写法大概就这样了。