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