iOS组件化casa方案的个人理解

1,485 阅读3分钟

casa老师的方案主要是基于Mediator模式和Target-Action模式,中间采用了runtime来完成调用。

下面以Demo为例,来分析一下组件化实施的方式。首先看一下Demo的项目结构:

CTMediator类就是中间件类,负责远程调用和本地组件间调用的处理和分发。

#import <UIKit/UIKit.h>

extern NSString * const kCTMediatorParamsKeySwiftTargetModuleName;

@interface CTMediator : NSObject

+ (instancetype)sharedInstance;

// 远程App调用入口
- (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion;
// 本地组件调用入口
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;
- (void)releaseCachedTargetWithTargetName:(NSString *)targetName;

@end

其中远程调用函数performActionWithUrl:completion:是对url进行处理解析后转化成本地调用。

看一下本地调用函数的代码实现:

NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName];
    
    // generate target
    NSString *targetClassString = nil;
    if (swiftModuleName.length > 0) {
        targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];
    } else {
        targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    }
    NSObject *target = self.cachedTarget[targetClassString];
    if (target == nil) {
        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;
    }
    
    if (shouldCacheTarget) {
        self.cachedTarget[targetClassString] = target;
    }

    if ([target respondsToSelector:action]) {
        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];
            [self.cachedTarget removeObjectForKey:targetClassString];
            return nil;
        }
    }

首先获取target实例。然后生成target要出发的消息SEL action,然后发送消息(调用函数)。

DemoModule文件夹即代表一个具体的组件(模块),下面简称AModule,Target-A类即这个组件(模块)对外暴露的可调用方法(对外接口)的具体实现,方法的实现都是基于组件内。那么当别的地方想调用AModule时怎么处理呢?并不是直接引用Target-A,如果这样就与AModule耦合在了一起,破坏了组件的独立性。既然是Mediator模式,当然是有中间件,我们看一下CTMediator+CTMediatorModuleAActions这个类。这里使用了分类来扩展CTMediator,增加了AModule的对外接口,保证了AModule的独立性。如果以后出现了BModule,只需要创建一个CTMediator+CTMediatorModuleBActions,来实现BModule的对外接口。

个人认为casa的组件化方案总体是优于蘑菇街的组件化方案的。但是对蘑菇街URLProtocol的方式加以改造,使用runtime的方式调用,就跟casa的方案接近一样了,大家可以试一下。至于openURL的方式,如果只支持本地调用,在Target-X中注册URL,是不是也类似Target-Action模式呢。

如果我的文章对你有所帮助,请留言告诉我,Thanks!