CTMediator 是一个中间人模式(Mediator Pattern)的完成,用于 iOS 组件化开发中的模块间通讯计划。

由于是非常热门的计划, 这边就来看看CTMediator 的详细完成与运用技巧

1.结构总架构

组件化方案 CTMediator 笔记.

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 需求保存一些状态信息,那么能够考虑运用缓存。否则,为了防止占用过多的内存,不应该运用缓存。

组件化方案 CTMediator 笔记.

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