在上篇文章我们讨论一下,url方式和缺点,我们现在来讨论通过,中间件来实现解耦。 主要是参考 CTMediator,来实现我们的组件化。笔者考虑到CTMediator 当做一个单例来处理(内部有个target缓存),我不太想这样处理,我觉得Mediator 就是一个 工具类,通过传递 组件的target 和 sel,其实就是一个加方法 就可以的。
中间件代码
这个中间件,其实就是一个动态解析的过程,把哪些组件的使用放到动态解析中,这里就不用注册了。
下面是笔者改造后的中间件,主要有三部分组成
- target: 目标组件,我们一边抽离一个工具类(使用
门面模式
) - action: 调用方法
- param: 传递参数,和使用回调
代码如下
//传递回调值
typedef void(^ZLMediatorCallBack)(id param);
extern NSString * const ZLMediatorCallBackKey; //可以放在 param 中
@interface ZLMediator : NSObject
// 本地组件调用入口
+ (id)Mediator_PerformTargetName:(NSString *)targetName
actionName:(NSString *)actionName
params:(NSDictionary *)params;
@end
NSString * const ZLMediatorCallBackKey = @"ZLMediatorCallBackKey";
@implementation ZLMediator
// 本地组件调用入口
+ (id)Mediator_PerformTargetName:(NSString *)targetName actionName:(NSString *)actionName params:(NSDictionary *)params {
Class targetClass = targetName.length > 0 ? NSClassFromString(targetName) : nil;
NSString * msg = [NSString stringWithFormat:@"不存在Target类名为:%@",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_%@ 不能响应 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.使用门面模式
来暴露组件的使用
@interface Detail_Target : NSObject
+ (void)Target_showWithParam:(NSDictionary *)param;
@end
// 门面模式 定义方法
@implementation Detail_Target
+ (void)Target_showWithParam:(NSDictionary *)param {
DetailComposite2 * detail = [[DetailComposite2 alloc] init];
detail.oneId = param[@"id"];
detail.name = param[@"name"];
// 执行组件的方法
[detail showComposite];
}
@end
然后使用就可以
[ZLMediator Mediator_PerformTargetName:@"Detail_Target"
actionName:@"Target_showWithParam:"
params:@{@"id":@"1", @"name":@"leeDev"}];
//打印出 showComposite2 _ id = 1 ; name = leeDev
Mediator 扩展
但是这样传递参数还是比较麻烦,所用我们可以使用 Media category 来简化我们的调用,让使用者更加明确
@interface ZLMediator (Detail)
//直接把 target 和 sel 和param 给屏蔽了,只给外界暴露 简单的接口
+ (void) detailShowWithId:(NSString *)id name:(NSString *)name;
@end
@implementation ZLMediator (Detail)
//直接把 target 和 sel 和param 给屏蔽了,只给外界暴露 简单的接口
+ (void) detailShowWithId:(NSString *)id name:(NSString *)name {
NSDictionary * param = @{@"id":id, @"name":name};
[ZLMediator Mediator_PerformTargetName:@"Detail_Target"
actionName:@"Target_showWithParam:"
params:param];
}
@end
测试和使用
[ZLMediator detailShowWithId:@"10" name:@"leeDev"];
// 打印出 showComposite2 _ id = 10 ; name = leeDev
显然相当于上一种方法直接调用,这个方法要简单明确多了,直接屏蔽了 target 和 sel 和param
.
优缺点
相对于蘑菇街的路由和协议
方式的架构,这个方式要强大多了
- 可以传递任意值
- 不需要注册,浪费内存
- 可以通过Mediator扩展,来定义更加清晰的接口给外界使用。
网友评论