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