一、路由模式
路由模式
register:先注册一个url,和一个registerBlock.
open:通过url,找到registerBlock,并执行。
![](https://img.haomeiwen.com/i1658883/99602dacb9741a1e.png)
Register
注册非常简单,就是path作为key, RegisterBlock作为value,保存在全局字典中。
-(void)registerURI:(NSString*)path event:(RegisterBlock)event
{
if(!_uriDict){
_uriDict = [[NSMutableDictionary alloc]init];
}
[_uriDict setObject:[event copy] forKey:path];
}
Open
打开方式,也很简单,就是通过path找到,registerBlock。 传入userInfo,执行registerBlock代码块。最后调用openBlock反馈结果。
-(void)openURI:(NSString*)path userInfo:(NSDictionary*)userInfo result:(OpenBlock)result
{
RegisterBlock event = [_uriDict objectForKey:path];
if(event){
event(userInfo,result);
}else{
NSLog(@"not exist !");
}
}
使用
[[FWURIRouter sharedInstance] registerURI:@"home/info" event:^(NSDictionary *userInfo, OpenBlock callBack) {
NSString *nick = userInfo[@"nick"];
NSNumber *age = userInfo[@"age"];
NSLog(@"excute block nick = %@, age = %@",nick,age);
if(callBack){
callBack(YES,nil,nil);
}
}];
[[FWURIRouter sharedInstance] openURI:@"home/info" userInfo:@{@"nick":@"QQ",@"age":@"10"} result:^(BOOL result, id recever, id info) {
if(result){
NSLog(@"成功");
}else{
NSLog(@"失败");
}
}];
注册时机
注册的代码,是集中写一个单独的文件里,在程序启动的时候执行?不是的。
这套机制通常是为了剥离组件间的相互依赖。组件A需要提供对外的方法,统一写在
FWURIRouter+A的类目中
我们在FWURIRouter中定义了一个宏方便迅速注册
#define URIRegisterEvent(__NAME__,__PATH__) \
-(void)autoRegister##__NAME__{ \
[self registerURI:__PATH__ event:^(NSDictionary * userInfo,OpenBlock result){ \
[self on##__NAME__##Action:userInfo result:result]; \
}]; \
} \
-(void)on##__NAME__##Action:(NSDictionary *)userInfo result:(OpenBlock)result \
FWURIRouter+A的类目中可以这样写
URIRegisterEvent(FunName, @"home/info")
{
NSString *nick = userInfo[@"nick"];
NSNumber *age = userInfo[@"age"];
NSLog(@"nick = %@, age = %@",nick,age);
if(result){
result(YES,nil,nil);
}
}
然后当我们每次调用open方法的时候,都去调用下面这个方法,就自动完成了注册
-(void)checkRegister
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(self.class, &methodCount);
if (methods && methodCount > 0) {
for (unsigned int i = 0; i < methodCount; i++) {
SEL selector = method_getName(methods[i]);
NSString *selectorName = NSStringFromSelector(selector);
if ([selectorName hasPrefix:@"autoRegister"]) {
NSLog(@"selectorName = %@",selectorName);
SEL selector = NSSelectorFromString(selectorName);
IMP imp = [self methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(self, selector);
}
}
}
if (methods) {
free(methods);
}
});
}
到这里,路由模式,就解说完毕了。最后总结一下,核心思想。
1 注册就是,保存 path: registerBlock的键值对
2 open,就是通过path取出registerBlock,并执行
3 自动注册的小技巧:open之前,runtime查找方法名是autoRegister前缀开头的,并直接全部执行,完成注册。
二、协议注册
模块之间通过协议沟通,模块B实现了一个协议P, 模块A通过协议P调用模块B的方法,从而A,B互不依赖。
注册Class
static NSMutableDictionary *__components = nil;
void FWRegisterComponent(Class cls)
{
if (!__components) {
__components = [NSMutableDictionary dictionary];
}
NSString *key = NSStringFromClass(cls);
[__components setObject:[NSNull null] forKey:key];
}
根据协议,取出Class
-(id)componentFor:(Protocol*)protocol
{
__block id instance;
dispatch_sync_eventQueue(^{
NSArray *keys = [__components allKeys];
for (NSString *key in keys) {
Class cls = NSClassFromString(key);
if([cls conformsToProtocol:protocol]){
id value = [__components objectForKey:key];
if(!value || [value isKindOfClass:[NSNull class]]){
value = [[cls alloc]init];
[__components setObject:value forKey:key];
}
instance = value;
}
}
});
return instance;
}
先注册
@interface ComponentA()<UserDetailProtocol>
@end
@implementation ComponentA
+ (void)load {
FWRegisterComponent(self);
}
-(UIViewController*)getUserDetailController
{
return [[UserDetailViewController alloc]init];
}
@end
再访问
UIViewController *controller = [FWComponent(UserDetailProtocol) getUserDetailController];
三、Target-Action
这种方式,不需要注册。
第一步:模块A的方法,全部暴露在TargetA中
-(UserDetailViewController*)createUserDetailViewController:(NSDictionary*)params
{
NSLog(@"%@",params);
return [UserDetailViewController new];
}
@end
第二步:硬编码TargetA
NSString * const kTarget = @"TargetA";
NSString * const kActionUserDetail = @"createUserDetailViewController:";
@implementation FWTargetAction (ModuleA)
-(UIViewController*)getUserDetail
{
return [self performTarget:kTarget action:kActionUserDetail params:@{@"info":@"targetAction"}];
}
@end
第三步:中心处理
@implementation FWTargetAction
+ (instancetype)sharedInstance
{
static FWTargetAction *mediator;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mediator = [[FWTargetAction alloc] init];
});
return mediator;
}
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params
{
if(!targetName || !actionName){
return nil;
}
Class targetClass = NSClassFromString(targetName);
NSObject *target = [[targetClass alloc]init];
SEL action = NSSelectorFromString(actionName);
return [self safePerformAction:action target:target params:params];
}
- (id)safePerformAction:(SEL)action target:(NSObject *)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
}
总结
路由模块,协议注册,Target-Action这三种模式都可以完成模块之间的沟通,解除模块间的相互依赖。
路由模块,设计简洁,类目拆分,使用简单明了。
协议注册,处了创建很多协议外,还需创建若干实现协议的类,不便于管理
Target-Action模式优点是,无需注册,用类目拆分,结构清晰。
所有代码,均来自https://github.com/FSilver/Component
网友评论