前兩篇文章主要是對(duì)這篇文章的內(nèi)容進(jìn)行了一個(gè)鋪墊翠储,這里就一起來(lái)看下
CTMediator
的實(shí)現(xiàn)原理
CTMediator
中最關(guān)鍵的一個(gè)方法:
- (id)performTarget:(NSString *)targetName action:(NSString *)actionNameparams:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;
看一下里面的具體實(shí)現(xiàn):
// 從 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];
}
// 根據(jù) targetClassString 從 cachedTarget (緩存的Target)獲取 target
NSObject *target = self.cachedTarget[targetClassString];
if (target == nil) {
// 未獲取到 則生成一個(gè)target
Class targetClass = NSClassFromString(targetClassString);
target = [[targetClass alloc] init];
}
// generate action
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
SEL action = NSSelectorFromString(actionString);
if (target == nil) {
// 這里是處理無(wú)響應(yīng)請(qǐng)求的地方之一,這個(gè)demo做得比較簡(jiǎn)單合搅,如果沒(méi)有可以響應(yīng)的target,就直接return了。實(shí)際開(kāi)發(fā)過(guò)程中是可以事先給一個(gè)固定的target專門(mén)用于在這個(gè)時(shí)候頂上,然后處理這種請(qǐng)求的
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
return nil;
}
// 是否需要對(duì) Target 進(jìn)行緩存
if (shouldCacheTarget) {
// 將 Target 進(jìn)行緩存
self.cachedTarget[targetClassString] = target;
}
// 判斷 Target 是否有 action 相應(yīng)暂题,避免crash
if ([target respondsToSelector:action]) {
// 這里是處理有響應(yīng)請(qǐng)求的地方
return [self safePerformAction:action target:target params:params];
} else {
// 這里是處理無(wú)響應(yīng)請(qǐng)求的地方,如果無(wú)響應(yīng)究珊,則嘗試調(diào)用對(duì)應(yīng)target的notFound方法統(tǒng)一處理
SEL action = NSSelectorFromString(@"notFound:");
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
} else {
// 這里也是處理無(wú)響應(yīng)請(qǐng)求的地方薪者,在notFound都沒(méi)有的時(shí)候,這個(gè)demo是直接return了剿涮。實(shí)際開(kāi)發(fā)過(guò)程中言津,可以用前面提到的固定的target頂上的。
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
// 刪除緩存的無(wú)用 Target
[self.cachedTarget removeObjectForKey:targetClassString];
return nil;
}
}
處理有響應(yīng)請(qǐng)求的地方會(huì)調(diào)用 - (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params
方法
// NSMethodSignature 該類為對(duì)方法的參數(shù)取试、返回類型進(jìn)行封裝
NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
if(methodSig == nil) {
return nil;
}
// 獲取返回類型
const char* retType = [methodSig methodReturnType];
// 判斷返回值 類型
if (strcmp(retType, @encode(void)) == 0) {
// 用來(lái)包裝方法和對(duì)應(yīng)的對(duì)象悬槽,它可以存儲(chǔ)方法的名稱,對(duì)應(yīng)的對(duì)象瞬浓,對(duì)應(yīng)的參數(shù)
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
// 執(zhí)行NSInvocation對(duì)象中指定對(duì)象的指定方法初婆,并且傳遞指定的參數(shù)
[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;
// 將返回?cái)?shù)據(jù)拷貝到提供的緩存區(qū)(retLoc)內(nèi)
[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對(duì)象傳遞消息,執(zhí)行 target 中 action 的方法猿棉,傳遞參數(shù) params
return [target performSelector:action withObject:params];
總結(jié):
CTMediator
的實(shí)現(xiàn)得益于RunTime的存在磅叛,正因?yàn)槿绱宋覀兛梢越怦罡鱾€(gè)模塊,實(shí)現(xiàn)App之間的模塊化萨赁,這應(yīng)該也是很多iOS開(kāi)發(fā)不愿意完全舍棄OC的原因吧弊琴。