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


