CTMediator 是一个中间人模式(Mediator Pattern)的实现,用于 iOS 组件化开发中的模块间通信方案。
因为是非常热门的方案, 这边就来看看CTMediator 的具体实现与使用技巧
1.框架总架构

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_User 的 action 时,将会使用这个缓存的实例,而不是再次创建新的实例。如果这个值为 NO,那么 CTMediator 每次都会创建新的 Target_User 实例。通常来说,如果 target 的创建和销毁开销很大,或者 target 需要保存一些状态信息,那么可以考虑使用缓存。否则,为了避免占用过多的内存,不应该使用缓存。

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 类的父类的方法列表中查找。
- 如果在所有的父类中都找不到,就会触发消息转发(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:¶ms 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
