CTMediator 是一個(gè)中間人模式(Mediator Pattern)的實(shí)現(xiàn),用于 iOS 組件化開發(fā)中的模塊間通信方案。
因?yàn)槭欠浅衢T的方案, 這邊就來(lái)看看CTMediator 的具體實(shí)現(xiàn)與使用技巧
1.框架總架構(gòu)

2.CTMediator 的基本使用方式
2.1. 在每個(gè)模塊中定義一個(gè) Target 類,這個(gè)類包含了模塊中需要提供給其他模塊調(diào)用的所有方法。每個(gè)方法對(duì)應(yīng)一個(gè) Action,方法的參數(shù)和返回值需要定義在一個(gè)字典(NSDictionary)中。
2.2.通過(guò) CTMediator 的 performTarget:action:params:shouldCacheTarget: 方法調(diào)用模塊中的方法。這個(gè)方法需要傳入目標(biāo)模塊(Target)的名稱、要調(diào)用的方法(Action)的名稱、方法參數(shù)以及是否需要緩存目標(biāo)模塊。
2.3. CTMediator 會(huì)在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建目標(biāo)模塊的實(shí)例,然后調(diào)用指定的方法,并將結(jié)果返回給調(diào)用者。
舉例實(shí)際使用
有一個(gè)用戶模塊,這個(gè)模塊提供了一個(gè)顯示用戶信息的頁(yè)面。
我們可以創(chuàng)建一個(gè) Target,例如叫做 Target_User,
然后在這個(gè) Target 中定義一個(gè) Action,例如叫做 Action_showUserInfo:,
這個(gè) Action 對(duì)應(yīng)一個(gè)方法,用于創(chuàng)建并顯示用戶信息頁(yè)面。方法的參數(shù)可能包含了用戶的 ID,例如 {@"userId" : @"123"}。
3.在其他模塊中,如果你需要顯示用戶信息頁(yè)面,我們可以這樣調(diào)用:
CTMediator *mediator = [CTMediator sharedInstance];
NSDictionary *params = @{@"userId" : @"123"};
UIViewController *userViewController = [mediator performTarget:@"User" action:@"showUserInfo" params:params shouldCacheTarget:NO];
// 然后可以將 userViewController 推入到導(dǎo)航控制器中
[self.navigationController pushViewController:userViewController animated:YES];
[mediator performTarget:@"User" action:@"showUserInfo" params:params shouldCacheTarget:NO]; 是使用 CTMediator 執(zhí)行一個(gè)操作。這個(gè)操作可能返回一個(gè)對(duì)象,這里是一個(gè) UIViewController 實(shí)例,也可能返回其他類型的對(duì)象,取決于具體的實(shí)現(xiàn)。下面是各個(gè)參數(shù)的作用:
"User":這是 target 的名稱,對(duì)應(yīng)的是 Target_User 類。這個(gè)類應(yīng)該在用戶模塊中定義,并包含了需要提供給其他模塊調(diào)用的所有方法。每個(gè)方法對(duì)應(yīng)一個(gè) action。
"showUserInfo":這是 action 的名稱,對(duì)應(yīng)的是 Target_User 類中的 Action_showUserInfo: 方法。這個(gè)方法被設(shè)計(jì)用來(lái)創(chuàng)建并返回一個(gè)顯示用戶信息的 UIViewController 實(shí)例。
params:這是傳遞給 action 的參數(shù)。參數(shù)需要封裝在一個(gè)字典中,例如 @{@"userId" : @"123"}。在這個(gè)例子中,字典包含了一個(gè)鍵為 "userId" 的項(xiàng),值為 "123"。這個(gè)值將被 Action_showUserInfo: 方法用來(lái)獲取用戶的信息。
NO:這個(gè)參數(shù)決定是否應(yīng)該緩存 target。如果這個(gè)值為 YES,那么 CTMediator 將會(huì)在第一次創(chuàng)建 Target_User 實(shí)例后,將這個(gè)實(shí)例緩存起來(lái)。以后再需要執(zhí)行 Target_User 的 action 時(shí),將會(huì)使用這個(gè)緩存的實(shí)例,而不是再次創(chuàng)建新的實(shí)例。如果這個(gè)值為 NO,那么 CTMediator 每次都會(huì)創(chuàng)建新的 Target_User 實(shí)例。通常來(lái)說(shuō),如果 target 的創(chuàng)建和銷毀開銷很大,或者 target 需要保存一些狀態(tài)信息,那么可以考慮使用緩存。否則,為了避免占用過(guò)多的內(nèi)存,不應(yīng)該使用緩存。

4.CTMediator涉及的 OC runtime 技術(shù)
主要在動(dòng)態(tài)獲取 target 類, 動(dòng)態(tài)創(chuàng)建 target 實(shí)例,以及動(dòng)態(tài)獲取 action 方法.
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{
//... 省略部分代碼
// 生成 target 類名
NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
// 動(dòng)態(tài)獲取 target 類
Class targetClass = NSClassFromString(targetClassString);
// 動(dòng)態(tài)創(chuàng)建 target 實(shí)例
NSObject *target = [[targetClass alloc] init];
// 生成 action 方法名
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
// 動(dòng)態(tài)獲取 action 方法
SEL action = NSSelectorFromString(actionString);
// 動(dòng)態(tài)調(diào)用 action 方法
if ([target respondsToSelector:action]) {
4.1動(dòng)態(tài)獲取 target 類
NSClassFromString 是一個(gè) Objective-C 的運(yùn)行時(shí)函數(shù),它可以根據(jù)提供的類名字符串動(dòng)態(tài)地獲取類的 Class 對(duì)象。它的參數(shù)是一個(gè)類名的字符串,返回值是一個(gè) Class 類型的對(duì)象。如果找不到對(duì)應(yīng)的類,它會(huì)返回 nil。
Class targetClass = NSClassFromString(targetClassString);
targetClassString 是一個(gè)包含了完整類名的字符串。NSClassFromString 會(huì)在運(yùn)行時(shí)查找有沒(méi)有這個(gè)名稱的類。如果找到了,它就返回這個(gè)類的 Class 對(duì)象;如果找不到,它就返回 nil。
動(dòng)態(tài)獲取類的能力是 Objective-C 的動(dòng)態(tài)特性的一部分,它讓 Objective-C 的行為可以在運(yùn)行時(shí)改變。這也是 CTMediator 能夠?qū)崿F(xiàn)模塊間解耦通信的關(guān)鍵所在:CTMediator可以在運(yùn)行時(shí)根據(jù)需要?jiǎng)討B(tài)地找到并調(diào)用任何模塊的任何方法,而無(wú)需在編譯時(shí)就確定這些信息。
注意: 由于 NSClassFromString 是基于字符串的,所以在使用它時(shí)需要小心確保類名的字符串是正確的。如果字符串有誤,NSClassFromString 可能會(huì)返回 nil,導(dǎo)致后續(xù)的操作失敗。
4.2 動(dòng)態(tài)獲取 action 方法
SEL action = NSSelectorFromString(actionString);
NSSelectorFromString 是 Objective-C 的一個(gè)運(yùn)行時(shí)函數(shù),它可以根據(jù)提供的方法名字符串(即選擇器名)動(dòng)態(tài)地獲取一個(gè) SEL 類型的選擇器。選擇器(selector)在 Objective-C 中是一種可以表示和調(diào)用方法的數(shù)據(jù)類型。
通過(guò)NSSelectorFromString 這樣一個(gè)動(dòng)態(tài)特性,CTMediator 只需要知道方法名的字符串,就可以調(diào)用任何模塊的任何方法,而無(wú)需在編譯時(shí)就知道這些信息。
在這行代碼中,actionString 是一個(gè)包含了完整方法名的字符串。
NSSelectorFromString 會(huì)在運(yùn)行時(shí)查找有沒(méi)有這個(gè)名稱的方法。如果找到了,它就返回這個(gè)方法的選擇器;如果找不到,它就返回 NULL。
4.2.2 衍生知識(shí)點(diǎn) SEL 是什么?
我們先介紹一下 OC 語(yǔ)言發(fā)送消息的機(jī)制.
當(dāng)在 OC 中向一個(gè)對(duì)象發(fā)送一個(gè)消息時(shí),運(yùn)行時(shí)系統(tǒng)會(huì)通過(guò)對(duì)象的 isa 指針找到類對(duì)象,然后在類對(duì)象的方法列表中查找與消息對(duì)應(yīng)的 SEL。如果找到了,就會(huì)獲取對(duì)應(yīng)的IMP,然后調(diào)用這個(gè)函數(shù)指針指向的代碼,執(zhí)行方法的實(shí)現(xiàn)。如果在類對(duì)象的方法列表中找不到,就會(huì)在其元類的方法列表中繼續(xù)查找,直到找到為止。如果在所有的超類中都找不到,就會(huì)觸發(fā)消息轉(zhuǎn)發(fā)(message forwarding)機(jī)制。
這個(gè)部分可以這樣圖示.
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" 的簡(jiǎn)寫,它是一個(gè)表示方法名的類型。在 Objective-C 中,當(dāng)你發(fā)送一個(gè)消息給一個(gè)對(duì)象時(shí),你其實(shí)是在告訴這個(gè)對(duì)象執(zhí)行一個(gè)selector。你可以把selector看作是方法名。每個(gè) selector在 Objective-C 的運(yùn)行時(shí)系統(tǒng)中都有一個(gè)唯一的地址,即使在不同的類中定義的完全相同的方法名,它們的 selector 地址也是相同的。
IMP:這是"implementation"(執(zhí)行)的簡(jiǎn)寫,它是一個(gè)函數(shù)指針,指向方法的實(shí)現(xiàn)。在 Objective-C 中,每個(gè)類的實(shí)例都有一個(gè)類對(duì)象,類對(duì)象中存儲(chǔ)了類的方法列表。每個(gè)方法列表的元素是一個(gè)結(jié)構(gòu)體,其中包含一個(gè) SEL 和一個(gè) IMP。
SEL 是方法名
IMP 是方法的實(shí)現(xiàn)。
4.2.3舉例說(shuō)明
例如,你有一個(gè) Person 對(duì)象,你向這個(gè)對(duì)象發(fā)送一個(gè) 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 對(duì)象收到 sayHello 消息。
2.運(yùn)行時(shí)系統(tǒng)通過(guò) Person 對(duì)象的 isa 指針找到 Person 類。
3.在 Person 類的方法列表中查找 sayHello 的選擇器(SEL)。
4.如果找到了,就獲取 sayHello 方法的實(shí)現(xiàn)(IMP)并執(zhí)行這個(gè)實(shí)現(xiàn)。
5.如果沒(méi)有找到,就在 Person 類的父類的方法列表中查找。
- 如果在所有的父類中都找不到,就會(huì)觸發(fā)消息轉(zhuǎn)發(fā)(Message Forwarding)機(jī)制。
4.2.5舉例升級(jí), 如何使用CTMediator 做這個(gè)消息發(fā)送.
// 創(chuàng)建一個(gè) CTMediator 實(shí)例
CTMediator *mediator = [CTMediator sharedInstance];
// 創(chuàng)建一個(gè)字典來(lái)存儲(chǔ)需要傳遞給 "sayHello" 方法的參數(shù)
NSDictionary *params = @{};
// 使用 CTMediator 的 performTarget:action:params:shouldCacheTarget: 方法發(fā)送 "sayHello" 消息
[mediator performTarget:@"Person" action:@"sayHello" params:params shouldCacheTarget:NO];
CTMediator 實(shí)例的 performTarget:action:params:shouldCacheTarget: 方法被用來(lái)發(fā)送 sayHello消息。這個(gè)方法的參數(shù)如下:
target:這是你想要發(fā)送消息的目標(biāo)對(duì)象的名稱。在這個(gè)例子中,目標(biāo)對(duì)象是 Person。
action:這是你想要執(zhí)行的方法的名稱。在這個(gè)例子中,你想要執(zhí)行的方法是 sayHello。
params:這是一個(gè)字典,包含你想要傳遞給目標(biāo)方法的參數(shù)。在這個(gè)例子中,sayHello 方法不需要任何參數(shù),所以這個(gè)字典是空的。
shouldCacheTarget:這是一個(gè)布爾值,決定是否緩存目標(biāo)對(duì)象。如果設(shè)置為 YES,CTMediator 會(huì)緩存目標(biāo)對(duì)象,以便下次可以快速找到它。如果設(shè)置為 NO,CTMediator 不會(huì)緩存目標(biāo)對(duì)象。
當(dāng)我們調(diào)用 performTarget:action:params:shouldCacheTarget: 方法時(shí),CTMediator 會(huì)在運(yùn)行時(shí)查找并調(diào)用 Person 對(duì)象的 sayHello 方法。這就是 CTMediator 的工作原理:它可以在運(yùn)行時(shí)動(dòng)態(tài)地找到并調(diào)用任何對(duì)象的任何方法,而無(wú)需在編譯時(shí)就確定這些信息。
下面是CTMediator去調(diào)用這個(gè)方法的流程圖.
+-----------------+ 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 的情況下,直接調(diào)用Person 的方法.
5 參數(shù)透?jìng)? 且支持 block 回調(diào)
使用 CTMediator 的過(guò)程中,方法block回調(diào)可以被定義為一個(gè) block,然后 將這個(gè) block作為參數(shù)傳遞給目標(biāo)方法。在目標(biāo)方法完成后,這個(gè)回調(diào) block 將被執(zhí)行,你可以在回調(diào) block 中接收和處理目標(biāo)方法的執(zhí)行結(jié)果。
// 創(chuàng)建 Calculator 對(duì)象
Calculator *calculator = [[Calculator alloc] init];
// 調(diào)用 `calculate:withCompletion:` 方法
[calculator calculate:@{@"input1": @10, @"input2": @20} withCompletion:^(NSInteger result) {
NSLog(@"The result is %ld", (long)result);
}];
// Calculator 類的實(shí)現(xiàn)
@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 來(lái)調(diào)用 calculate:withCompletion: 方法并獲取計(jì)算結(jié)果,代碼如下:
// 創(chuàng)建一個(gè) CTMediator 實(shí)例
CTMediator *mediator = [CTMediator sharedInstance];
// 創(chuàng)建一個(gè)字典來(lái)存儲(chǔ)需要傳遞給 `calculate:withCompletion:` 方法的參數(shù)
NSDictionary *params = @{
@"input1": @10,
@"input2": @20,
@"callback": ^(NSInteger result) {
NSLog(@"The result is %ld", (long)result);
}
};
// 使用 CTMediator 的 `performTarget:action:params:shouldCacheTarget:` 方法發(fā)送消息
[mediator performTarget:@"Calculator" action:@"calculate:withCompletion:" params:params shouldCacheTarget:NO];
創(chuàng)建了一個(gè)包含三個(gè)鍵值對(duì)的 params 字典:
"input1" 的值是 @10。
"input2" 的值是 @20。
"callback" 的值是一個(gè) block,這個(gè) block 會(huì)在 calculate:withCompletion:方法完成后被調(diào)用,并接收計(jì)算結(jié)果作為參數(shù)。
當(dāng) performTarget:action:params:shouldCacheTarget:方法被調(diào)用時(shí),CTMediator 會(huì)動(dòng)態(tài)地找到名為 Calculator 的類,然后在這個(gè)類中查找 calculate:withCompletion:方法。如果找到了這個(gè)方法,CTMediator 就會(huì)創(chuàng)建一個(gè) Calculator 實(shí)例,然后調(diào)用這個(gè)實(shí)例的calculate:withCompletion:方法,并把 params字典作為參數(shù)傳遞給這個(gè)方法。
在 calculate:withCompletion: 方法中,你可以從 params 字典中取出你需要的參數(shù),例如:
- (void)calculate:(NSDictionary *)params withCompletion:(void (^)(NSInteger))completion {
NSNumber *input1 = params[@"input1"];
NSNumber *input2 = params[@"input2"];
void (^callback)(NSInteger) = params[@"callback"];
// 進(jìn)行計(jì)算
NSInteger result = input1.integerValue + input2.integerValue;
// 調(diào)用回調(diào) block
if (callback) {
callback(result);
}
}
通過(guò)這個(gè)字典可以將 block 也一起傳入.
6. 緩存機(jī)制
緩存目標(biāo)類實(shí)例,避免重復(fù)初始化,優(yōu)化性能。
當(dāng)我們通過(guò) CTMediator 請(qǐng)求一個(gè)目標(biāo)并執(zhí)行一個(gè)動(dòng)作時(shí),CTMediator 會(huì)首先查看是否已經(jīng)創(chuàng)建并緩存了這個(gè)目標(biāo)的實(shí)例。如果已經(jīng)創(chuàng)建了,那么 CTMediator 就直接使用這個(gè)已經(jīng)創(chuàng)建的實(shí)例;如果還沒(méi)有創(chuàng)建,那么 CTMediator 就會(huì)創(chuàng)建一個(gè)新的實(shí)例,然后把這個(gè)新創(chuàng)建的實(shí)例緩存起來(lái),以供后續(xù)使用。
這種目標(biāo)緩存的機(jī)制可以幫助避免重復(fù)初始化目標(biāo)實(shí)例,從而提高程序的性能。
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{
// 獲取 target 類名字符串
NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
// 嘗試從緩存中獲取 target 實(shí)例
NSObject *target = [self safeFetchCachedTarget:targetClassString];
if (target == nil) {
// 如果緩存中沒(méi)有找到 target 實(shí)例,則創(chuàng)建一個(gè)新的實(shí)例
Class targetClass = NSClassFromString(targetClassString);
target = [[targetClass alloc] init];
}
if (shouldCacheTarget) {
// 如果 shouldCacheTarget 參數(shù)為 YES,則將新創(chuàng)建的 target 實(shí)例緩存起來(lái)
[self safeSetCachedTarget:target key:targetClassString];
}
}
safeFetchCachedTarget: 和 safeSetCachedTarget:key: 這兩個(gè)方法在 CTMediator 中用于獲取和設(shè)置緩存的目標(biāo)實(shí)例。
- (NSObject *)safeFetchCachedTarget:(NSString *)key {
@synchronized (self) {
return self.cachedTarget[key];
}
}
- (void)safeSetCachedTarget:(NSObject *)target key:(NSString *)key {
@synchronized (self) {
self.cachedTarget[key] = target;
}
}
safeFetchCachedTarget: 方法通過(guò)給定的鍵從緩存中獲取對(duì)應(yīng)的目標(biāo)實(shí)例。它是線程安全的,因?yàn)樗褂昧?@synchronized 關(guān)鍵字來(lái)確保在多線程環(huán)境下的安全訪問(wèn):
self.cachedTarget 這個(gè)屬性,它是一個(gè) NSMutableDictionary 類型的字典,用于存儲(chǔ)緩存的目標(biāo)實(shí)例。這個(gè)字典的鍵是目標(biāo)類名的字符串形式,而值是對(duì)應(yīng)的目標(biāo)實(shí)例。
7.異常處理
當(dāng)我們嘗試調(diào)用一個(gè)目標(biāo)的某個(gè)動(dòng)作時(shí),CTMediator會(huì)首先檢查這個(gè)目標(biāo)是否存在,然后檢查這個(gè)目標(biāo)是否響應(yīng)這個(gè)動(dòng)作。如果目標(biāo)不存在或者不響應(yīng)這個(gè)動(dòng)作,CTMediator 就會(huì)調(diào)用 NoTargetActionResponseWithTargetString:selectorString:originParams:方法來(lái)處理這個(gè)異常。在這個(gè)方法中,你可以根據(jù)你的需要來(lái)定義如何處理這種異常,例如,可以輸出一個(gè)錯(cuò)誤提示,或者調(diào)用一個(gè)備用的目標(biāo)或動(dòng)作。
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{
// ... 其他代碼 ...
if (target == nil) {
// 如果目標(biāo)不存在,則調(diào)用 `NoTargetActionResponseWithTargetString:selectorString:originParams:` 方法來(lái)處理異常
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
return nil;
}
if ([target respondsToSelector:action]) {
// 如果目標(biāo)響應(yīng)這個(gè)動(dòng)作,則正常執(zhí)行這個(gè)動(dòng)作
return [self safePerformAction:action target:target params:params];
} else {
// 如果目標(biāo)不響應(yīng)這個(gè)動(dòng)作,則調(diào)用 `NoTargetActionResponseWithTargetString:selectorString:originParams:` 方法來(lái)處理異常
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
return nil;
}
}
8.容易擴(kuò)展
CTMediator 作為中介者模式的實(shí)現(xiàn),其核心職責(zé)是負(fù)責(zé)組件之間的通信。但是因?yàn)樵O(shè)計(jì)比較輕便,比較靈活,CTMediator 也可以被擴(kuò)展來(lái)實(shí)現(xiàn)各種輔助方法,從而增強(qiáng)其功能。
核心方法 - (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
{
// 檢查參數(shù)的有效性
if (![self isValidParams:params]) {
NSLog(@"Invalid parameters: %@", params);
return nil;
}
// 檢查執(zhí)行結(jié)果
id result = [self safePerformAction:action target:target params:params];
if (result == nil) {
// 如果執(zhí)行失敗,則進(jìn)行錯(cuò)誤處理
[self handleErrorWithTargetName:targetName actionName:actionName params:params];
}
}
// 檢查參數(shù)的有效性
- (BOOL)isValidParams:(NSDictionary *)params {
// 在這里,我們簡(jiǎn)單地假設(shè)所有的參數(shù)都必須是 NSString 類型
for (id value in params.allValues) {
if (![value isKindOfClass:[NSString class]]) {
return NO;
}
}
return YES;
}
// 處理錯(cuò)誤
- (void)handleErrorWithTargetName:(NSString *)targetName actionName:(NSString *)actionName params:(NSDictionary *)params {
NSLog(@"Failed to perform action %@ on target %@ with parameters: %@", actionName, targetName, params);
}
最后. 是CTMediator 的源碼注釋, 認(rèn)真看看.
// 引入 CTMediator 頭文件
#import "CTMediator.h"
// 引入 Objective-C 運(yùn)行時(shí)頭文件,用于動(dòng)態(tài)調(diào)用方法等
#import <objc/runtime.h>
// 引入 CoreGraphics 頭文件,用于處理圖形相關(guān)的操作
#import <CoreGraphics/CoreGraphics.h>
// 定義一個(gè)常量字符串,用于獲取 Swift 模塊名稱
NSString * const kCTMediatorParamsKeySwiftTargetModuleName = @"kCTMediatorParamsKeySwiftTargetModuleName";
// CTMediator 的接口聲明
@interface CTMediator ()
// 聲明一個(gè)屬性,用于存儲(chǔ)已緩存的 target
@property (nonatomic, strong) NSMutableDictionary *cachedTarget;
@end
// CTMediator 的實(shí)現(xiàn)
@implementation CTMediator
// 公共方法
// 獲取 CTMediator 的單例
+ (instancetype)sharedInstance
{
// 聲明一個(gè)靜態(tài)變量用于存儲(chǔ)單例對(duì)象
static CTMediator *mediator;
// 聲明一個(gè) dispatch_once_t 變量,用于保證單例創(chuàng)建的線程安全
static dispatch_once_t onceToken;
// 使用 GCD 的 dispatch_once 函數(shù)創(chuàng)建單例
dispatch_once(&onceToken, ^{
mediator = [[CTMediator alloc] init];
// 初始化 cachedTarget,避免多線程重復(fù)初始化
[mediator cachedTarget];
});
// 返回單例對(duì)象
return mediator;
}
// 通過(guò) URL 執(zhí)行 action,并將結(jié)果通過(guò) completion 回調(diào)返回
- (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion
{
// 檢查 url 是否為空或者不是 NSURL 類型
if (url == nil || ![url isKindOfClass:[NSURL class]]) {
return nil;
}
// 創(chuàng)建一個(gè) NSMutableDictionary 用于存儲(chǔ)參數(shù)
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
// 使用 NSURLComponents 解析 url
NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:url.absoluteString];
// 遍歷所有的參數(shù)并存入 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);
}
// 執(zhí)行 target-action,并將結(jié)果返回
id result = [self performTarget:url.host action:actionName params:params shouldCacheTarget:NO];
// 如果有 completion 回調(diào),執(zhí)行回調(diào)
if (completion) {
if (result) {
completion(@{@"result":result});
} else {
completion(nil);
}
}
// 返回結(jié)果
return result;
}
// 執(zhí)行 target-action,并將結(jié)果返回
- (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_目標(biāo)名"
if (swiftModuleName.length > 0) {
targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];
} else {
// 否則 targetClassString 為 "Target_目標(biāo)名"
targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
}
// 從緩存中獲取 target
NSObject *target = [self safeFetchCachedTarget:targetClassString];
// 如果緩存中沒(méi)有 target,創(chuàng)建并緩存 target
if (target == nil) {
Class targetClass = NSClassFromString(targetClassString);
target = [[targetClass alloc] init];
}
// 生成 action
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
// 通過(guò) actionString 創(chuàng)建一個(gè) SEL
SEL action = NSSelectorFromString(actionString);
// 如果 target 不存在,調(diào)用 NoTargetActionResponseWithTargetString:selectorString:originParams: 方法處理
if (target == nil) {
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
return nil;
}
// 如果需要緩存 target,將 target 緩存起來(lái)
if (shouldCacheTarget) {
[self safeSetCachedTarget:target key:targetClassString];
}
// 如果 target 能響應(yīng) action,執(zhí)行 action 并返回結(jié)果
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
} else {
// 如果 target 不能響應(yīng) action,嘗試調(diào)用 notFound: 方法
SEL action = NSSelectorFromString(@"notFound:");
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
} else {
// 如果還是無(wú)法響應(yīng),調(diào)用 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_目標(biāo)名" 對(duì)應(yīng)的 Class 是否存在
if (moduleName.length > 0) {
return NSClassFromString([NSString stringWithFormat:@"%@.Target_%@", moduleName, targetName]) != nil;
} else {
// 否則返回 "Target_目標(biāo)名" 對(duì)應(yīng)的 Class 是否存在
return NSClassFromString([NSString stringWithFormat:@"Target_%@", targetName]) != nil;
}
}
// 私有方法
// 處理無(wú)法響應(yīng) action 的情況
- (void)NoTargetActionResponseWithTargetString:(NSString *)targetString selectorString:(NSString *)selectorString originParams:(NSDictionary *)originParams
{
// 創(chuàng)建一個(gè) "Action_response:" 的 SEL
SEL action = NSSelectorFromString(@"Action_response:");
// 創(chuàng)建一個(gè) "Target_NoTargetAction" 的 target
NSObject *target = [[NSClassFromString(@"Target_NoTargetAction") alloc] init];
// 創(chuàng)建一個(gè) params 字典,包含原始的參數(shù)、target 名和 selector 名
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
params[@"originParams"] = originParams;
params[@"targetString"] = targetString;
params[@"selectorString"] = selectorString;
// 執(zhí)行 action 并傳入 params
[self safePerformAction:action target:target params:params];
}
// 安全執(zhí)行 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];
// 根據(jù)返回類型,創(chuàng)建一個(gè) NSInvocation,并設(shè)置參數(shù)、selector 和 target
// 如果返回類型是 void,執(zhí)行 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,執(zhí)行 action 并返回結(jié)果
// 如果返回類型是 id,執(zhí)行 action 并返回結(jié)果
// 注意,這里省略了具體的代碼,需要根據(jù)實(shí)際的返回類型寫出相應(yīng)的代碼
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
// 如果返回類型不是上面的任何一種,直接執(zhí)行 action 并返回結(jié)果
return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
}
// 獲取和設(shè)置方法
// 獲取 cachedTarget
- (NSMutableDictionary *)cachedTarget
{
// 如果 cachedTarget 不存在,創(chuàng)建 cachedTarget
if (_cachedTarget == nil) {
_cachedTarget = [[NSMutableDictionary alloc] init];
}
// 返回 cachedTarget
return _cachedTarget;
}
// 從緩存中獲取 target
- (NSObject *)safeFetchCachedTarget:(NSString *)key {
// 使用 @synchronized 來(lái)保證線程安全
@synchronized (self) {
// 從 cachedTarget 中獲取指定的 target
return self.cachedTarget[key];
}
}
// 將 target 緩存起來(lái)
- (void)safeSetCachedTarget:(NSObject *)target key:(NSString *)key {
// 使用 @synchronized 來(lái)保證線程安全
@synchronized (self) {
// 將 target 緩存到 cachedTarget 中
self.cachedTarget[key] = target;
}
}
@end