组件化

一、路由模式

路由模式
register:先注册一个url,和一个registerBlock.
open:通过url,找到registerBlock,并执行。


test1.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()

@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

你可能感兴趣的:(组件化)