Android架构师iOS 技能

使用CTMediators实现组件化探索

2020-07-24  本文已影响0人  小白猿

1.组件化基本分层

2.业务模块

  1. 通常项目模块间的关系大致如下图


3.CTMediator 通讯中间框架介绍

4.增加分类

image.png

4.1总体介绍

尽管CTMediator的代码整体比较少,逻辑也不是很复杂,但是由于是避免对三方框架强耦合,也避免框架的代码发生过大的变化,另外增加分类也是为了进一步解耦合,总结一下大致为以下几点

4.2 CTMediator代码 和 demo介绍

下面我们针对在github给出的demo进行介绍

1. 项目整体介绍
2. 这个功能是将 DemoModuleADetailViewController modal出来,并需要往这个控制器里传递一个UIImage,DemoModuleADetailViewController 一下简称为模块A
@interface DemoModuleADetailViewController : UIViewController
@property (nonatomic, strong, readonly) UILabel *valueLabel;
@property (nonatomic, strong, readonly) UIImageView *imageView;
@end
- (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;
}
3. 第二步解决了方法名的问题,然后我们整体看下方法的调用,从cell的点击一步一步
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{
    // tagrt判空
    if (targetName == nil || actionName == nil) {
        return nil;
    }
    // 对swift的特殊标记
    NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName];
    
    // 拼接target-action的tagert,就是方法调用的类名的字符串,类名规则为Target_方法名
    NSString *targetClassString = nil;
    if (swiftModuleName.length > 0) {
        targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];
    } else {
        // 类名规则为Target_方法名
        targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    }
    
    // 做了类对象的缓存,避免多次创建对象
    NSObject *target = self.cachedTarget[targetClassString];
    // 类对象
    if (target == nil) {
        Class targetClass = NSClassFromString(targetClassString);
        target = [[targetClass alloc] init];
    }
    // 处理方法名字 拼接规则为 Action_方法名
    NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
    // 方法名转 SEL
    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;
        }
    }
}
- (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params
{
    NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
    if(methodSig == nil) {
        return nil;
    }
    const char* retType = [methodSig methodReturnType];

    if (strcmp(retType, @encode(void)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        return nil;
    }

    if (strcmp(retType, @encode(NSInteger)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        NSInteger result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }

    if (strcmp(retType, @encode(BOOL)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        BOOL result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }

    if (strcmp(retType, @encode(CGFloat)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        CGFloat result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }

    if (strcmp(retType, @encode(NSUInteger)) == 0) {
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
        [invocation setArgument:&params atIndex:2];
        [invocation setSelector:action];
        [invocation setTarget:target];
        [invocation invoke];
        NSUInteger result = 0;
        [invocation getReturnValue:&result];
        return @(result);
    }

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
}

至此一个完整CTM调用就结束了,大致的流程为


上一篇 下一篇

猜你喜欢

热点阅读