在上篇文章我們討論一下,url方式和缺點(diǎn)权均,我們現(xiàn)在來(lái)討論通過(guò),中間件來(lái)實(shí)現(xiàn)解耦髓绽。 主要是參考 CTMediator,來(lái)實(shí)現(xiàn)我們的組件化敛苇。筆者考慮到CTMediator 當(dāng)做一個(gè)單例來(lái)處理(內(nèi)部有個(gè)target緩存),我不太想這樣處理顺呕,我覺(jué)得Mediator 就是一個(gè) 工具類(lèi)枫攀,通過(guò)傳遞 組件的target 和 sel,其實(shí)就是一個(gè)加方法 就可以的株茶。
中間件代碼
這個(gè)中間件来涨,其實(shí)就是一個(gè)動(dòng)態(tài)解析的過(guò)程,把哪些組件的使用放到動(dòng)態(tài)解析中启盛,這里就不用注冊(cè)了蹦掐。
下面是筆者改造后的中間件,主要有三部分組成
- target: 目標(biāo)組件僵闯,我們一邊抽離一個(gè)工具類(lèi)(使用
門(mén)面模式
) - action: 調(diào)用方法
- param: 傳遞參數(shù)卧抗,和使用回調(diào)
代碼如下
//傳遞回調(diào)值
typedef void(^ZLMediatorCallBack)(id param);
extern NSString * const ZLMediatorCallBackKey; //可以放在 param 中
@interface ZLMediator : NSObject
// 本地組件調(diào)用入口
+ (id)Mediator_PerformTargetName:(NSString *)targetName
actionName:(NSString *)actionName
params:(NSDictionary *)params;
@end
NSString * const ZLMediatorCallBackKey = @"ZLMediatorCallBackKey";
@implementation ZLMediator
// 本地組件調(diào)用入口
+ (id)Mediator_PerformTargetName:(NSString *)targetName actionName:(NSString *)actionName params:(NSDictionary *)params {
Class targetClass = targetName.length > 0 ? NSClassFromString(targetName) : nil;
NSString * msg = [NSString stringWithFormat:@"不存在Target類(lèi)名為:%@",targetName];
NSAssert(targetClass, msg);
SEL action = actionName.length > 0 ? NSSelectorFromString(actionName) : nil;
msg = [NSString stringWithFormat:@"Target_%@ 不存在 action為:%@",targetName,actionName];
NSAssert(action, msg);
if ([targetClass respondsToSelector:action]) {
return [self SafePerformAction:action target:targetClass params:params];
} else {
msg = [NSString stringWithFormat:@"Target_%@ 不能響應(yīng) action_%@",targetName,actionName];
NSAssert(0, msg);
}
return nil;
}
+ (id)SafePerformAction:(SEL)action target:(id)target params:(NSDictionary *)params {
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];
[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;
[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);
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
}
@end
方便使用中間件
1.使用門(mén)面模式
來(lái)暴露組件的使用
@interface Detail_Target : NSObject
+ (void)Target_showWithParam:(NSDictionary *)param;
@end
// 門(mén)面模式 定義方法
@implementation Detail_Target
+ (void)Target_showWithParam:(NSDictionary *)param {
DetailComposite2 * detail = [[DetailComposite2 alloc] init];
detail.oneId = param[@"id"];
detail.name = param[@"name"];
// 執(zhí)行組件的方法
[detail showComposite];
}
@end
然后使用就可以
[ZLMediator Mediator_PerformTargetName:@"Detail_Target"
actionName:@"Target_showWithParam:"
params:@{@"id":@"1", @"name":@"leeDev"}];
//打印出 showComposite2 _ id = 1 ; name = leeDev
Mediator 擴(kuò)展
但是這樣傳遞參數(shù)還是比較麻煩,所用我們可以使用 Media category 來(lái)簡(jiǎn)化我們的調(diào)用鳖粟,讓使用者更加明確
@interface ZLMediator (Detail)
//直接把 target 和 sel 和param 給屏蔽了社裆,只給外界暴露 簡(jiǎn)單的接口
+ (void) detailShowWithId:(NSString *)id name:(NSString *)name;
@end
@implementation ZLMediator (Detail)
//直接把 target 和 sel 和param 給屏蔽了,只給外界暴露 簡(jiǎn)單的接口
+ (void) detailShowWithId:(NSString *)id name:(NSString *)name {
NSDictionary * param = @{@"id":id, @"name":name};
[ZLMediator Mediator_PerformTargetName:@"Detail_Target"
actionName:@"Target_showWithParam:"
params:param];
}
@end
測(cè)試和使用
[ZLMediator detailShowWithId:@"10" name:@"leeDev"];
// 打印出 showComposite2 _ id = 10 ; name = leeDev
顯然相當(dāng)于上一種方法直接調(diào)用向图,這個(gè)方法要簡(jiǎn)單明確多了泳秀,直接屏蔽了 target 和 sel 和param
.
優(yōu)缺點(diǎn)
相對(duì)于蘑菇街的路由和協(xié)議
方式的架構(gòu)标沪,這個(gè)方式要強(qiáng)大多了
- 可以傳遞任意值
- 不需要注冊(cè),浪費(fèi)內(nèi)存
- 可以通過(guò)Mediator擴(kuò)展嗜傅,來(lái)定義更加清晰的接口給外界使用金句。