美文网首页
CTMediator理解

CTMediator理解

作者: 哥只是个菜鸟 | 来源:发表于2021-07-06 12:29 被阅读0次

1.CTMediator作为路由中间件跳转,很大程度解决了各种文件依赖,更加灵活,比较适用于电商活动等需要各种跳转页面的项目

2.我们需要在外面定义好路由的url和安卓端统一,比如:xxx://shop/detail?id=1234

3.新建一个类,名字按照路由的格式定义,比如:Target_shop,shop表示你要跳转的页面,我这边是购物车页面,里面的函数命名也按照路由定义,比如Action_detail,这个页面暂时没有入参,最终根据传入的url调用这里的方法拿到控制器进行跳转

UIViewController *vc = [[CTMediator sharedInstance] performActionWithUrl:url completion:nil];
[self.navigationController pushViewController:vc animated:YES];
@interface Target_shop : NSObject

- (UIViewController *)Action_detail:(NSDictionary *)params;

@end
@implementation Target_shop

- (UIViewController *)Action_detail:(NSDictionary *)params {
    ShopCarViewController *vc = [[ShopCarViewController alloc] init];
    vc.hidesBottomBarWhenPushed = YES;
    return vc;
}

@end

4.源码实现

  • 1.主要还是通过反射机制找到类名Target_shop 和 方法名 Action_detail
 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 safeFetchCachedTarget:targetClassString];
    if (target == nil) {
        Class targetClass = NSClassFromString(targetClassString);
        target = [[targetClass alloc] init];
    }

    // generate action
    NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
    SEL action = NSSelectorFromString(actionString);
  • 2.找到类和方法之后就可以通过 NSInvocation去调用对应的方法,判断它是void,NSInteger,BOOL,CGFloat,NSUInteger,否则使用performSelector , performSelector只能传递2个参数,可以把多个参数封装成NSDictionary,然后进行传递;NSInvocation能传递多个
// 1.通过target和action生成方法前方法签名
NSMethodSignature* methodSig = [target methodSignatureForSelector:action];

// 2.通过方法签名生成NSInvaocation
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];

// 3.设置参数位置在第3个位置,有默认值两个参数 self _cmd
[invocation setArgument:&params atIndex:2];

// 4.设置action
[invocation setSelector:action];

// 5.设置target
[invocation setTarget:target];

// 6.开始调用
[invocation invoke];


 id result;
// 7.获得方法的返回值
[invocation getReturnValue:&result];

消息转发流程

  • 第一步动态解析,当前类中如果没有实现这个方法时,调用这个方法就会首先走这个方法,我们可以在这里做容错处理,动态添加一个方法进行,这样就不会crash,第一步成功也就不会再走后面的转发流程了
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(requestOne1)) {
        IMP sayOIMP = class_getMethodImplementation(self, @selector(requestOne));
        Method sayOMethod = class_getClassMethod(self, @selector(requestOne));
        const char *sayOType = method_getTypeEncoding(sayOMethod);
        return class_addMethod(self, sel, sayOIMP, sayOType);
    }
    return [super resolveClassMethod:sel];
}
- (void)requestOne {
    
}
  • 第二步快速转发,当第一步没找到方法时,就会接着调用这个方法,这个时候我们可以动态返回一个包含相同方法的对象,也就是把消息转发给另一个对象,让他代替当前类去完成方法实现,不管是不是我们自己方法接受者实现的,只要有这个方法的imp, 程序就不会崩溃,同理如果成功就不会走后面的慢速转发了

- (id)forwardingTargetForSelector:(SEL)aSelector {//快速转发
    if ([NSStringFromSelector(aSelector) isEqualToString:@"requestOne"]) {
        return [ViewController2 alloc];
    }
    return [super forwardingTargetForSelector:aSelector];
}
  • 第三步慢速转发,我们可以手动返回一个签名,这样就会继续走后面的forwardInvocation方法,2个方法搭配使用的,NSInvocation进行方法调用
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    NSLog(@"%s -- %@",__func__,NSStringFromSelector(aSelector));
    if (aSelector == @selector(requestOne)) { // v @ :
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return [super methodSignatureForSelector:aSelector];
}

//
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"%s ",__func__);
    SEL aSelector = [anInvocation selector];

    if ([[ViewController2 alloc] respondsToSelector:aSelector]) {
        [anInvocation invokeWithTarget:[ViewController2 alloc]];
    } else {
        [super forwardInvocation:anInvocation];
    }
}
  • 一般我们在hook的时候在第三步做处理,如果找不到实现的方法必然会走到这一步,在线上避免crash就好了,大概率也不会出现这种情况以防万一
- (NSMethodSignature *)hook_methodSignatureForSelector:(SEL)aSelector {
    NSMethodSignature* signature = [self hook_methodSignatureForSelector:aSelector];
    if (signature) {
        return signature;
    }
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

- (void)hook_forwardInvocation:(NSInvocation *)anInvocation {
    NSString *message = [NSString
        stringWithFormat:@"Unrecognized instance class:%@ and selector:%@", NSStringFromClass(self.class), NSStringFromSelector(anInvocation.selector)];
    NSLog(@"%@",message);
}

runtime实现模型和字典互转

  • 字典转模型,利用runtime的消息发送objc_msgSend,给每一个属性发送set消息赋值
  • 模型转字典,也是发消息,给每一个属性发送get消息得到value
  • 注意编译objc_msgSend会报错,之前的xocde版本都是要在BuildSetting里面设置objc_msgSend那行==NO,后面发现设置了也没用


    image.png
  • 函数是无返回参数的函数,需要把它强制转化类型
((id (*)(id _Nullable, SEL _Nonnull,
                     id))(void *)objc_msgSend)(self, selector, value);
#import "DemoModel.h"
#import <objc/message.h>
#import <objc/runtime.h>

@implementation DemoModel

- (instancetype)dictionaryToModel:(NSDictionary *)dictionary {
    NSArray *keyArr = [dictionary allKeys];
    for (int i = 0; i < keyArr.count; i++) {
        NSString *key = keyArr[I];
        id value = [dictionary valueForKey:key];
        //key首字母大写
        NSString *setName = [NSString stringWithFormat:@"set%@:",key.capitalizedString];
        //生成set方法
        SEL selector = NSSelectorFromString(setName);
        if ([self respondsToSelector:selector]) {
            ((id (*)(id _Nullable, SEL _Nonnull,
                     id))(void *)objc_msgSend)(self, selector, value);
            //objc_msgSend函数是无返回参数的函数,需要把它强制转化类型
        } else {
            NSLog(@"生成%@set方法失败", key.capitalizedString);
        }
    }
    return self;
}

- (NSDictionary *)modelToDictionary {
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    unsigned int count = 0;
    objc_property_t *propertys = class_copyPropertyList(self.class, &count);
    for (int i = 0; i < count; i++) {
        objc_property_t property = propertys[I];
        const char *name = property_getName(property);
        NSString *properyName = [NSString stringWithUTF8String:name];
        //生成get方法
        SEL selector = NSSelectorFromString(properyName);
        if ([self respondsToSelector:selector]) {
            id value = ((id (*)(id _Nullable, SEL _Nonnull))(void *)objc_msgSend)(self, selector);
            [dict setValue:value forKey:properyName];
        } else {
            NSLog(@"没找到该属性%@",properyName);
        }
    }
    free(propertys);
    return dict;
}


@end

相关文章

  • CTMediator理解

    1.CTMediator作为路由中间件跳转,很大程度解决了各种文件依赖,更加灵活,比较适用于电商活动等需要各种跳转...

  • iOS 源码分析(一):CTMediator

    本文的主要目的是分析CTMediator以及其使用 CTMediator简介 CTMediator[https:/...

  • iOS 组件化(二)

    利用CTMediator的组件化通过target-action操作。 先看下如何使用CTMediator的。 相关...

  • 组件化一些见解

    前言 这篇文章主要是对MGJRouter和CTMediator组件框架调研之后写的介绍与理解。主要也是市面比较主流...

  • CTMediator实现原理

    概述 基本构成:Target_XX、CTMediator、CTMediator_XX分类,XX为模块名称,可以用s...

  • CTMediator

    基本概念 首先,对于 iOS 这种面向对象编程的开发模式来说,我们应该遵循以下五个原则,即 SOLID 原则[ht...

  • 关于模块化,组件化

    1.按业务分模块,按功能分组件 CTMediator的方案 各个模块通过CTMediator中的target-ac...

  • iOS组件化第二步:使用中间者模式(CTMediator)

    那么如何去实现这个中间者模式呢,casa 大神的CTMediator可以很好的解决这个问题。CTMediator通...

  • 第三方SDK简介

    YYModel dsbridge SensorsAnalyticsSDK CTMediator TangFileT...

  • CTMediator学习

    开始接手公司的新项目,里面是组件化写的,一脸懵,开始记录下学习过程,顺便归纳总价,大佬请飘过~文章在不间断更新中,...

网友评论

      本文标题:CTMediator理解

      本文链接:https://www.haomeiwen.com/subject/rmtgultx.html