美文网首页iOS程序猿
组件化方案 CTMediator 笔记.

组件化方案 CTMediator 笔记.

作者: DingGa | 来源:发表于2023-07-18 14:40 被阅读0次

    CTMediator 是一个中间人模式(Mediator Pattern)的实现,用于 iOS 组件化开发中的模块间通信方案。

    因为是非常热门的方案, 这边就来看看CTMediator 的具体实现与使用技巧

    1.框架总架构

    image.png

    2.CTMediator 的基本使用方式

    2.1. 在每个模块中定义一个 Target 类,这个类包含了模块中需要提供给其他模块调用的所有方法。每个方法对应一个 Action,方法的参数和返回值需要定义在一个字典(NSDictionary)中。

    2.2.通过 CTMediator 的 performTarget:action:params:shouldCacheTarget: 方法调用模块中的方法。这个方法需要传入目标模块(Target)的名称、要调用的方法(Action)的名称、方法参数以及是否需要缓存目标模块。

    2.3. CTMediator 会在运行时动态创建目标模块的实例,然后调用指定的方法,并将结果返回给调用者。

    举例实际使用

    有一个用户模块,这个模块提供了一个显示用户信息的页面。
    我们可以创建一个 Target,例如叫做 Target_User
    然后在这个 Target 中定义一个 Action,例如叫做 Action_showUserInfo:
    这个 Action 对应一个方法,用于创建并显示用户信息页面。方法的参数可能包含了用户的 ID,例如 {@"userId" : @"123"}

    3.在其他模块中,如果你需要显示用户信息页面,我们可以这样调用:
    CTMediator *mediator = [CTMediator sharedInstance];
    NSDictionary *params = @{@"userId" : @"123"};
    UIViewController *userViewController = [mediator performTarget:@"User" action:@"showUserInfo" params:params shouldCacheTarget:NO];
    
    // 然后可以将 userViewController 推入到导航控制器中
    [self.navigationController pushViewController:userViewController animated:YES];
    
    

    [mediator performTarget:@"User" action:@"showUserInfo" params:params shouldCacheTarget:NO]; 是使用 CTMediator 执行一个操作。这个操作可能返回一个对象,这里是一个 UIViewController 实例,也可能返回其他类型的对象,取决于具体的实现。下面是各个参数的作用:

    "User":这是 target 的名称,对应的是 Target_User 类。这个类应该在用户模块中定义,并包含了需要提供给其他模块调用的所有方法。每个方法对应一个 action。

    "showUserInfo":这是 action 的名称,对应的是 Target_User 类中的 Action_showUserInfo: 方法。这个方法被设计用来创建并返回一个显示用户信息的 UIViewController 实例。

    params:这是传递给 action 的参数。参数需要封装在一个字典中,例如 @{@"userId" : @"123"}。在这个例子中,字典包含了一个键为 "userId" 的项,值为 "123"。这个值将被 Action_showUserInfo: 方法用来获取用户的信息。

    NO:这个参数决定是否应该缓存 target。如果这个值为 YES,那么 CTMediator 将会在第一次创建 Target_User 实例后,将这个实例缓存起来。以后再需要执行 Target_Useraction 时,将会使用这个缓存的实例,而不是再次创建新的实例。如果这个值为 NO,那么 CTMediator 每次都会创建新的 Target_User 实例。通常来说,如果 target 的创建和销毁开销很大,或者 target 需要保存一些状态信息,那么可以考虑使用缓存。否则,为了避免占用过多的内存,不应该使用缓存。

    调用交互逻辑图示.png

    4.CTMediator涉及的 OC runtime 技术

    主要在动态获取 target 类, 动态创建 target 实例,以及动态获取 action 方法.

    - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
    {
        //... 省略部分代码
    
        // 生成 target 类名
        NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
        // 动态获取 target 类
        Class targetClass = NSClassFromString(targetClassString);
        // 动态创建 target 实例
        NSObject *target = [[targetClass alloc] init];
    
        // 生成 action 方法名
        NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
        // 动态获取 action 方法
        SEL action = NSSelectorFromString(actionString);
    
        // 动态调用 action 方法
        if ([target respondsToSelector:action]) {
    
    4.1动态获取 target 类

    NSClassFromString 是一个 Objective-C 的运行时函数,它可以根据提供的类名字符串动态地获取类的 Class 对象。它的参数是一个类名的字符串,返回值是一个 Class 类型的对象。如果找不到对应的类,它会返回 nil。

    Class targetClass = NSClassFromString(targetClassString);
    

    targetClassString 是一个包含了完整类名的字符串。NSClassFromString 会在运行时查找有没有这个名称的类。如果找到了,它就返回这个类的 Class 对象;如果找不到,它就返回 nil

    动态获取类的能力是 Objective-C 的动态特性的一部分,它让 Objective-C 的行为可以在运行时改变。这也是 CTMediator 能够实现模块间解耦通信的关键所在:CTMediator可以在运行时根据需要动态地找到并调用任何模块的任何方法,而无需在编译时就确定这些信息。

    注意: 由于 NSClassFromString 是基于字符串的,所以在使用它时需要小心确保类名的字符串是正确的。如果字符串有误,NSClassFromString 可能会返回 nil,导致后续的操作失败。

    4.2 动态获取 action 方法
        SEL action = NSSelectorFromString(actionString);
    

    NSSelectorFromString 是 Objective-C 的一个运行时函数,它可以根据提供的方法名字符串(即选择器名)动态地获取一个 SEL 类型的选择器。选择器(selector)在 Objective-C 中是一种可以表示和调用方法的数据类型。
    通过NSSelectorFromString 这样一个动态特性,CTMediator 只需要知道方法名的字符串,就可以调用任何模块的任何方法,而无需在编译时就知道这些信息。

    在这行代码中,actionString 是一个包含了完整方法名的字符串。
    NSSelectorFromString 会在运行时查找有没有这个名称的方法。如果找到了,它就返回这个方法的选择器;如果找不到,它就返回 NULL

    4.2.2 衍生知识点 SEL 是什么?

    我们先介绍一下 OC 语言发送消息的机制.

    当在 OC 中向一个对象发送一个消息时,运行时系统会通过对象的 isa 指针找到类对象,然后在类对象方法列表中查找与消息对应的 SEL。如果找到了,就会获取对应的IMP,然后调用这个函数指针指向的代码,执行方法的实现。如果在类对象方法列表中找不到,就会在其元类方法列表中继续查找,直到找到为止。如果在所有的超类中都找不到,就会触发消息转发(message forwarding)机制。
    这个部分可以这样图示.

               NSObject               NSObject's class              NSObject's meta-class
    +--------------+               +--------------+               +--------------+
    | isa          | -------------> | isa          | -------------> | isa          |
    |              |               | superclass   |               | superclass   |
    |              |               | cache        |               | cache        |
    |              |               | vtable       |               | vtable       |
    |              |               | sarray       |               | sarray       |
    |              |               | class_ro     |               | class_ro     |
    |              |               | class_rw     |               | class_rw     |
    +--------------+               | method_list  |               | method_list  |
                                   |              |               |              |
                                   | SEL  |  IMP  |               | SEL  |  IMP  |
                                   |------+-------|               |------+-------|
                                   | foo  |  *foo |               | foo  |  *foo |
                                   | bar  |  *bar |               | bar  |  *bar |
                                   +--------------+               +--------------+
    
    

    SEL:这是"selector" 的简写,它是一个表示方法名的类型。在 Objective-C 中,当你发送一个消息给一个对象时,你其实是在告诉这个对象执行一个selector。你可以把selector看作是方法名。每个 selector在 Objective-C 的运行时系统中都有一个唯一的地址,即使在不同的类中定义的完全相同的方法名,它们的 selector 地址也是相同的

    IMP:这是"implementation"(执行)的简写,它是一个函数指针,指向方法的实现。在 Objective-C 中,每个类的实例都有一个类对象类对象中存储了类的方法列表。每个方法列表元素是一个结构体,其中包含一个 SEL 和一个 IMP

    SEL 是方法名
    IMP 是方法的实现

    4.2.3举例说明

    例如,你有一个 Person 对象,你向这个对象发送一个 sayHello 消息:
    Person *person = [[Person alloc] init];
    [person sayHello];

    +-----------------+     message      +-----------------+      SEL       +-----------------+      IMP       +-----------------+
    |  Person Object  | ----------------> |  Person Class  | -------------> |  Method List    | -------------> |  sayHello Code  |
    |                 |                  |                |                |                 |                |                 |
    |  receive        |                  |  search for    |                |  "sayHello"     |                |  printf("Hello |
    |  "sayHello"     |                  |  "sayHello"    |                |  ----> IMP      |                |  World!\n");   |
    |  message        |                  +-----------------+                +-----------------+                +-----------------+
            |                             |  (if not found)
            |                             |  Search in superclass
            v                             v
    +-----------------+           +-----------------+
    |  Start          |           |  Message        |
    |  [person         |           |  Forwarding     |
    |  sayHello];     |           |                 |
    +-----------------+           +-----------------+
    
    
    4.2.4流程解释

    1.Person 对象收到 sayHello 消息。
    2.运行时系统通过 Person 对象的 isa 指针找到 Person 类。
    3.在 Person 类的方法列表中查找 sayHello 的选择器(SEL)。
    4.如果找到了,就获取 sayHello 方法的实现(IMP)并执行这个实现。
    5.如果没有找到,就在 Person 类的父类的方法列表中查找。

    1. 如果在所有的父类中都找不到,就会触发消息转发(Message Forwarding)机制。
    4.2.5举例升级, 如何使用CTMediator 做这个消息发送.
    // 创建一个 CTMediator 实例
    CTMediator *mediator = [CTMediator sharedInstance];
    
    // 创建一个字典来存储需要传递给 "sayHello" 方法的参数
    NSDictionary *params = @{};
    
    // 使用 CTMediator 的 performTarget:action:params:shouldCacheTarget: 方法发送 "sayHello" 消息
    [mediator performTarget:@"Person" action:@"sayHello" params:params shouldCacheTarget:NO];
    
    

    CTMediator 实例的 performTarget:action:params:shouldCacheTarget: 方法被用来发送 sayHello消息。这个方法的参数如下:

    target:这是你想要发送消息的目标对象的名称。在这个例子中,目标对象是 Person。
    action:这是你想要执行的方法的名称。在这个例子中,你想要执行的方法是 sayHello。
    params:这是一个字典,包含你想要传递给目标方法的参数。在这个例子中,sayHello 方法不需要任何参数,所以这个字典是空的。
    shouldCacheTarget:这是一个布尔值,决定是否缓存目标对象。如果设置为 YES,CTMediator 会缓存目标对象,以便下次可以快速找到它。如果设置为 NO,CTMediator 不会缓存目标对象。

    当我们调用 performTarget:action:params:shouldCacheTarget: 方法时,CTMediator 会在运行时查找并调用 Person 对象的 sayHello 方法。这就是 CTMediator 的工作原理:它可以在运行时动态地找到并调用任何对象的任何方法,而无需在编译时就确定这些信息。

    下面是CTMediator去调用这个方法的流程图.

    +-----------------+        message        +-----------------+       SEL       +-----------------+       IMP       +-----------------+
    |  CTMediator     | ------------------->  |  Person Class   | ------------->  |  Method List    | ------------->  |  sayHello Code  |
    |                 |                       |                 |                 |                 |                 |                 |
    |  performTarget: |                       |  search for     |                 |  "sayHello"     |                 |  printf("Hello |
    |  "Person"       |                       |  "sayHello"     |                 |  ----> IMP      |                 |  World!\n");   |
    |  action:        |                       +-----------------+                 +-----------------+                 +-----------------+
    |  "sayHello"     |                       |  (if not found)
    |  params:        |                       |  Search in superclass
    |  {}             |                       v
    |  shouldCache    |               +-----------------+
    |  Target: NO     |               |  Message        |
    +-----------------+               |  Forwarding     |
                                      |                 |
                                      +-----------------+
    
    

    这样就可以在不知道Person class 的情况下,直接调用Person 的方法.

    5 参数透传, 且支持 block 回调

    使用 CTMediator 的过程中,方法block回调可以被定义为一个 block,然后 将这个 block作为参数传递给目标方法。在目标方法完成后,这个回调 block 将被执行,你可以在回调 block 中接收和处理目标方法的执行结果。

    // 创建 Calculator 对象
    Calculator *calculator = [[Calculator alloc] init];
    
    // 调用 `calculate:withCompletion:` 方法
    [calculator calculate:@{@"input1": @10, @"input2": @20} withCompletion:^(NSInteger result) {
        NSLog(@"The result is %ld", (long)result);
    }];
    
    
    // Calculator 类的实现
    @implementation Calculator
    
    - (void)calculate:(NSDictionary *)params withCompletion:(void (^)(NSInteger))completion {
        NSNumber *input1 = params[@"input1"];
        NSNumber *input2 = params[@"input2"];
        NSInteger result = input1.integerValue + input2.integerValue;
        
        if (completion) {
            completion(result);
        }
    }
    
    @end
    
    
    

    然后,我们可以使用 CTMediator 来调用 calculate:withCompletion: 方法并获取计算结果,代码如下:

    // 创建一个 CTMediator 实例
    CTMediator *mediator = [CTMediator sharedInstance];
    
    // 创建一个字典来存储需要传递给 `calculate:withCompletion:` 方法的参数
    NSDictionary *params = @{
        @"input1": @10,
        @"input2": @20,
        @"callback": ^(NSInteger result) {
            NSLog(@"The result is %ld", (long)result);
        }
    };
    
    // 使用 CTMediator 的 `performTarget:action:params:shouldCacheTarget:` 方法发送消息
    [mediator performTarget:@"Calculator" action:@"calculate:withCompletion:" params:params shouldCacheTarget:NO];
    

    创建了一个包含三个键值对的 params 字典:

    "input1" 的值是 @10。
    "input2" 的值是 @20。
    "callback" 的值是一个 block,这个 block 会在 calculate:withCompletion:方法完成后被调用,并接收计算结果作为参数。
    performTarget:action:params:shouldCacheTarget:方法被调用时,CTMediator 会动态地找到名为 Calculator 的类,然后在这个类中查找 calculate:withCompletion:方法。如果找到了这个方法,CTMediator 就会创建一个 Calculator 实例,然后调用这个实例的calculate:withCompletion:方法,并把 params字典作为参数传递给这个方法。

    在 calculate:withCompletion: 方法中,你可以从 params 字典中取出你需要的参数,例如:

    - (void)calculate:(NSDictionary *)params withCompletion:(void (^)(NSInteger))completion {
        NSNumber *input1 = params[@"input1"];
        NSNumber *input2 = params[@"input2"];
        void (^callback)(NSInteger) = params[@"callback"];
        
        // 进行计算
        NSInteger result = input1.integerValue + input2.integerValue;
        
        // 调用回调 block
        if (callback) {
            callback(result);
        }
    }
    

    通过这个字典可以将 block 也一起传入.

    6. 缓存机制

    缓存目标类实例,避免重复初始化,优化性能。

    当我们通过 CTMediator 请求一个目标并执行一个动作时,CTMediator 会首先查看是否已经创建并缓存了这个目标的实例。如果已经创建了,那么 CTMediator 就直接使用这个已经创建的实例;如果还没有创建,那么 CTMediator 就会创建一个新的实例,然后把这个新创建的实例缓存起来,以供后续使用。

    这种目标缓存的机制可以帮助避免重复初始化目标实例,从而提高程序的性能。

    - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
    {
        // 获取 target 类名字符串
        NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
    
        // 尝试从缓存中获取 target 实例
        NSObject *target = [self safeFetchCachedTarget:targetClassString];
        
        if (target == nil) {
            // 如果缓存中没有找到 target 实例,则创建一个新的实例
            Class targetClass = NSClassFromString(targetClassString);
            target = [[targetClass alloc] init];
        }
        
        if (shouldCacheTarget) {
            // 如果 shouldCacheTarget 参数为 YES,则将新创建的 target 实例缓存起来
            [self safeSetCachedTarget:target key:targetClassString];
        }
        
    }
    

    safeFetchCachedTarget: 和 safeSetCachedTarget:key: 这两个方法在 CTMediator 中用于获取和设置缓存的目标实例。

    - (NSObject *)safeFetchCachedTarget:(NSString *)key {
        @synchronized (self) {
            return self.cachedTarget[key];
        }
    }
    
    - (void)safeSetCachedTarget:(NSObject *)target key:(NSString *)key {
        @synchronized (self) {
            self.cachedTarget[key] = target;
        }
    }
    
    

    safeFetchCachedTarget: 方法通过给定的键从缓存中获取对应的目标实例。它是线程安全的,因为它使用了 @synchronized 关键字来确保在多线程环境下的安全访问:

    self.cachedTarget 这个属性,它是一个 NSMutableDictionary 类型的字典,用于存储缓存的目标实例。这个字典的键是目标类名的字符串形式,而值是对应的目标实例。

    7.异常处理

    当我们尝试调用一个目标的某个动作时,CTMediator会首先检查这个目标是否存在,然后检查这个目标是否响应这个动作。如果目标不存在或者不响应这个动作,CTMediator 就会调用 NoTargetActionResponseWithTargetString:selectorString:originParams:方法来处理这个异常。在这个方法中,你可以根据你的需要来定义如何处理这种异常,例如,可以输出一个错误提示,或者调用一个备用的目标或动作。

    - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
    {
        // ... 其他代码 ...
        
        if (target == nil) {
            // 如果目标不存在,则调用 `NoTargetActionResponseWithTargetString:selectorString:originParams:` 方法来处理异常
            [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
            return nil;
        }
        
        if ([target respondsToSelector:action]) {
            // 如果目标响应这个动作,则正常执行这个动作
            return [self safePerformAction:action target:target params:params];
        } else {
            // 如果目标不响应这个动作,则调用 `NoTargetActionResponseWithTargetString:selectorString:originParams:` 方法来处理异常
            [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
            return nil;
        }
    }
    
    8.容易扩展

    CTMediator 作为中介者模式的实现,其核心职责是负责组件之间的通信。但是因为设计比较轻便,比较灵活,CTMediator 也可以被扩展来实现各种辅助方法,从而增强其功能。

    核心方法 - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget

    - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
    {
       // 检查参数的有效性
        if (![self isValidParams:params]) {
            NSLog(@"Invalid parameters: %@", params);
            return nil;
        }
        
     // 检查执行结果
        id result = [self safePerformAction:action target:target params:params];
        if (result == nil) {
            // 如果执行失败,则进行错误处理
            [self handleErrorWithTargetName:targetName actionName:actionName params:params];
        }
    }
    
    
    // 检查参数的有效性
    - (BOOL)isValidParams:(NSDictionary *)params {
        // 在这里,我们简单地假设所有的参数都必须是 NSString 类型
        for (id value in params.allValues) {
            if (![value isKindOfClass:[NSString class]]) {
                return NO;
            }
        }
        
        return YES;
    }
    
    // 处理错误
    - (void)handleErrorWithTargetName:(NSString *)targetName actionName:(NSString *)actionName params:(NSDictionary *)params {
        NSLog(@"Failed to perform action %@ on target %@ with parameters: %@", actionName, targetName, params);
    }
    

    最后. 是CTMediator 的源码注释, 认真看看.

    // 引入 CTMediator 头文件
    #import "CTMediator.h"
    // 引入 Objective-C 运行时头文件,用于动态调用方法等
    #import <objc/runtime.h>
    // 引入 CoreGraphics 头文件,用于处理图形相关的操作
    #import <CoreGraphics/CoreGraphics.h>
    
    // 定义一个常量字符串,用于获取 Swift 模块名称
    NSString * const kCTMediatorParamsKeySwiftTargetModuleName = @"kCTMediatorParamsKeySwiftTargetModuleName";
    
    // CTMediator 的接口声明
    @interface CTMediator ()
    
    // 声明一个属性,用于存储已缓存的 target
    @property (nonatomic, strong) NSMutableDictionary *cachedTarget;
    
    @end
    
    // CTMediator 的实现
    @implementation CTMediator
    
    // 公共方法
    
    // 获取 CTMediator 的单例
    + (instancetype)sharedInstance
    {
        // 声明一个静态变量用于存储单例对象
        static CTMediator *mediator;
        // 声明一个 dispatch_once_t 变量,用于保证单例创建的线程安全
        static dispatch_once_t onceToken;
        // 使用 GCD 的 dispatch_once 函数创建单例
        dispatch_once(&onceToken, ^{
            mediator = [[CTMediator alloc] init];
            // 初始化 cachedTarget,避免多线程重复初始化
            [mediator cachedTarget];
        });
        // 返回单例对象
        return mediator;
    }
    
    // 通过 URL 执行 action,并将结果通过 completion 回调返回
    - (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion
    {
        // 检查 url 是否为空或者不是 NSURL 类型
        if (url == nil || ![url isKindOfClass:[NSURL class]]) {
            return nil;
        }
        
        // 创建一个 NSMutableDictionary 用于存储参数
        NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
        // 使用 NSURLComponents 解析 url
        NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:url.absoluteString];
        // 遍历所有的参数并存入 params
        [urlComponents.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if (obj.value && obj.name) {
                [params setObject:obj.value forKey:obj.name];
            }
        }];
        
        // 从 url 的 path 中获取 action 名称,并将前面的 "/" 删除
        NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
        // 如果 actionName 以 "native" 开头,返回 NO
        if ([actionName hasPrefix:@"native"]) {
            return @(NO);
        }
        
        // 执行 target-action,并将结果返回
        id result = [self performTarget:url.host action:actionName params:params shouldCacheTarget:NO];
        // 如果有 completion 回调,执行回调
        if (completion) {
            if (result) {
                completion(@{@"result":result});
            } else {
                completion(nil);
            }
        }
        // 返回结果
        return result;
    }
    
    // 执行 target-action,并将结果返回
    - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
    {
        // 检查 targetName 和 actionName 是否为空
        if (targetName == nil || actionName == nil) {
            return nil;
        }
        
        // 从 params 中获取 Swift 模块名
        NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName];
        
        // 生成 target
        NSString *targetClassString = nil;
        // 如果有 Swift 模块名,那么 targetClassString 为 "模块名.Target_目标名"
        if (swiftModuleName.length > 0) {
            targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];
        } else {
            // 否则 targetClassString 为 "Target_目标名"
            targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
        }
        // 从缓存中获取 target
        NSObject *target = [self safeFetchCachedTarget:targetClassString];
        // 如果缓存中没有 target,创建并缓存 target
        if (target == nil) {
            Class targetClass = NSClassFromString(targetClassString);
            target = [[targetClass alloc] init];
        }
    
        // 生成 action
        NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
        // 通过 actionString 创建一个 SEL
        SEL action = NSSelectorFromString(actionString);
        
        // 如果 target 不存在,调用 NoTargetActionResponseWithTargetString:selectorString:originParams: 方法处理
        if (target == nil) {
            [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
            return nil;
        }
        
        // 如果需要缓存 target,将 target 缓存起来
        if (shouldCacheTarget) {
            [self safeSetCachedTarget:target key:targetClassString];
        }
    
        // 如果 target 能响应 action,执行 action 并返回结果
        if ([target respondsToSelector:action]) {
            return [self safePerformAction:action target:target params:params];
        } else {
            // 如果 target 不能响应 action,尝试调用 notFound: 方法
            SEL action = NSSelectorFromString(@"notFound:");
            if ([target respondsToSelector:action]) {
                return [self safePerformAction:action target:target params:params];
            } else {
                // 如果还是无法响应,调用 NoTargetActionResponseWithTargetString:selectorString:originParams: 方法处理,并移除缓存的 target
                [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
                @synchronized (self) {
                    [self.cachedTarget removeObjectForKey:targetClassString];
                }
                return nil;
            }
        }
    }
    
    // 释放缓存的 target
    - (void)releaseCachedTargetWithFullTargetName:(NSString *)fullTargetName
    {
        // 如果 fullTargetName 为空,直接返回
        if (fullTargetName == nil) {
            return;
        }
        // 移除缓存的 target
        @synchronized (self) {
            [self.cachedTarget removeObjectForKey:fullTargetName];
        }
    }
    
    // 检查指定的 target 和 module 是否存在
    - (BOOL)check:(NSString * _Nullable)targetName moduleName:(NSString * _Nullable)moduleName{
        // 如果有 module 名,返回 "模块名.Target_目标名" 对应的 Class 是否存在
        if (moduleName.length > 0) {
            return NSClassFromString([NSString stringWithFormat:@"%@.Target_%@", moduleName, targetName]) != nil;
        } else {
            // 否则返回 "Target_目标名" 对应的 Class 是否存在
            return NSClassFromString([NSString stringWithFormat:@"Target_%@", targetName]) != nil;
        }
    }
    
    // 私有方法
    
    // 处理无法响应 action 的情况
    - (void)NoTargetActionResponseWithTargetString:(NSString *)targetString selectorString:(NSString *)selectorString originParams:(NSDictionary *)originParams
    {
        // 创建一个 "Action_response:" 的 SEL
        SEL action = NSSelectorFromString(@"Action_response:");
        // 创建一个 "Target_NoTargetAction" 的 target
        NSObject *target = [[NSClassFromString(@"Target_NoTargetAction") alloc] init];
        
        // 创建一个 params 字典,包含原始的参数、target 名和 selector 名
        NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
        params[@"originParams"] = originParams;
        params[@"targetString"] = targetString;
        params[@"selectorString"] = selectorString;
        
        // 执行 action 并传入 params
        [self safePerformAction:action target:target params:params];
    }
    
    // 安全执行 action
    - (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params
    {
        // 获取 action 的方法签名
        NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
        // 如果方法签名不存在,返回 nil
        if(methodSig == nil) {
            return nil;
        }
        // 获取返回类型
        const char* retType = [methodSig methodReturnType];
    
        // 根据返回类型,创建一个 NSInvocation,并设置参数、selector 和 target
        // 如果返回类型是 void,执行 action 并返回 nil
        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;
        }
    
        // 如果返回类型是 NSInteger、BOOL、CGFloat 或 NSUInteger,执行 action 并返回结果
        // 如果返回类型是 id,执行 action 并返回结果
        // 注意,这里省略了具体的代码,需要根据实际的返回类型写出相应的代码
    
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        // 如果返回类型不是上面的任何一种,直接执行 action 并返回结果
        return [target performSelector:action withObject:params];
    #pragma clang diagnostic pop
    }
    
    // 获取和设置方法
    
    // 获取 cachedTarget
    - (NSMutableDictionary *)cachedTarget
    {
        // 如果 cachedTarget 不存在,创建 cachedTarget
        if (_cachedTarget == nil) {
            _cachedTarget = [[NSMutableDictionary alloc] init];
        }
        // 返回 cachedTarget
        return _cachedTarget;
    }
    
    // 从缓存中获取 target
    - (NSObject *)safeFetchCachedTarget:(NSString *)key {
        // 使用 @synchronized 来保证线程安全
        @synchronized (self) {
            // 从 cachedTarget 中获取指定的 target
            return self.cachedTarget[key];
        }
    }
    
    // 将 target 缓存起来
    - (void)safeSetCachedTarget:(NSObject *)target key:(NSString *)key {
        // 使用 @synchronized 来保证线程安全
        @synchronized (self) {
            // 将 target 缓存到 cachedTarget 中
            self.cachedTarget[key] = target;
        }
    }
    
    @end
    
    

    相关文章

      网友评论

        本文标题:组件化方案 CTMediator 笔记.

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