前兩篇文章主要是對這篇文章的內容進行了一個鋪墊,這裏就一塊兒來看下
CTMediator
的實現原理 ,CTMediator
是一個單例,主要是基於Mediator
模式和Target-Action
模式,中間採用了runtime
來完成調用swift
CTMediator
提供的API分別爲:遠程app調用入口
、本地組件調用入口
、釋放某個target緩存
這裏主要介紹 本地組件調用入口
也是咱們最經常使用的一個方法:緩存
- (id)performTarget:(NSString *)targetName action:(NSString *)actionNameparams:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;
複製代碼
實現分析:bash
// 從 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
方法app
// 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選擇器,而後最終調用到目標業務提供的邏輯,完成需求。ui