前兩篇文章主要是對這篇文章的內容進行了一個鋪墊,這裡就一起來看下
CTMediator
的實現原理 ,CTMediator
是一個單例,主要是基於Mediator
模式和Target-Action
模式,中間採用了runtime
來完成呼叫
CTMediator
提供的API分別為:遠端app呼叫入口
、本地元件呼叫入口
、釋放某個target快取
這裡主要介紹 本地元件呼叫入口
也是我們最常用的一個方法:
- (id)performTarget:(NSString *)targetName action:(NSString *)actionNameparams:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;
複製程式碼
實現分析:
// 從 params 字典中 獲取 swiftModuleName
NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName];
// generate target
NSString *targetClassString = nil;
if (swiftModuleName.length > 0) {
targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName];
} else {
targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
}
// 根據 targetClassString 從 cachedTarget (快取的Target)獲取 target
NSObject *target = self.cachedTarget[targetClassString];
if (target == nil) {
// 未獲取到 則通過NSClassFromString將字串轉為應的類
Class targetClass = NSClassFromString(targetClassString);
target = [[targetClass alloc] init];
}
// generate action
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
SEL action = NSSelectorFromString(actionString);
if (target == nil) {
// 這裡是處理無響應請求的地方之一,這個demo做得比較簡單,如果沒有可以響應的target,就直接return了。實際開發過程中是可以事先給一個固定的target專門用於在這個時候頂上,然後處理這種請求的
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
return nil;
}
// 是否需要對 Target 進行快取
if (shouldCacheTarget) {
// 將 Target 進行快取
self.cachedTarget[targetClassString] = target;
}
// 判斷target物件是否響應action,避免crash
if ([target respondsToSelector:action]) {
// 這裡是處理有響應請求的地方
return [self safePerformAction:action target:target params:params];
} else {
// 這裡是處理無響應請求的地方,如果無響應,則嘗試呼叫對應target的notFound方法統一處理
SEL action = NSSelectorFromString(@"notFound:");
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
} else {
// 這裡也是處理無響應請求的地方,在notFound都沒有的時候,這個demo是直接return了。實際開發過程中,可以用前面提到的固定的target頂上的。
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
// 刪除快取的無用 Target
[self.cachedTarget removeObjectForKey:targetClassString];
return nil;
}
}
複製程式碼
處理有響應請求的地方會呼叫 - (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params
方法
// NSMethodSignature 記錄著某個方法的返回值型別資訊以及引數型別資訊。用於轉發訊息接收者無法響應的訊息
NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
if(methodSig == nil) {
return nil;
}
// 獲取返回型別
const char* retType = [methodSig methodReturnType];
// 判斷返回值 型別
if (strcmp(retType, @encode(void)) == 0) {
// 用來包裝方法和對應的物件,它可以儲存方法的名稱,對應的物件,對應的引數
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
// 執行NSInvocation物件中指定物件的指定方法,並且傳遞指定的引數
[invocation invoke];
return nil;
}
if (strcmp(retType, @encode(NSInteger)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
NSInteger result = 0;
// 將返回資料拷貝到提供的快取區(retLoc)內
[invocation getReturnValue:&result];
return @(result);
}
if (strcmp(retType, @encode(BOOL)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
BOOL result = 0;
[invocation getReturnValue:&result];
return @(result);
}
if (strcmp(retType, @encode(CGFloat)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
CGFloat result = 0;
[invocation getReturnValue:&result];
return @(result);
}
if (strcmp(retType, @encode(NSUInteger)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
NSUInteger result = 0;
[invocation getReturnValue:&result];
return @(result);
}
// 利用RunTime 向target物件傳遞訊息,執行 target 中 action 的方法,傳遞引數 params
return [target performSelector:action withObject:params];
複製程式碼
總結:
CTMediator
根據獲得的target和action資訊,通過objective-C的runtime轉化生成target例項以及對應的action選擇器,然後最終呼叫到目標業務提供的邏輯,完成需求。