iOS 组件化方案 —— 路由Router

2023-06-19  本文已影响0人  大成小栈

Github地址:
https://github.com/DachengWang/XFRouter

比较典型的路由实现方案,如下:

Router.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

// 组件对外公开接口, m组件名, i接口名, p(arg)接收参数, c(callback)回调block
#define XFROUTER_EXTERN_METHOD(m, i, p, c) + (id) routerHandle_##m##_##i:(NSDictionary*)arg callback:(id)callback

@interface XFRouter : NSObject

+(void)openURL:(NSString *)stringUrl arg:(NSDictionary *)param error:(NSError *)error callBack:(void (^)(void))callBack;

@end

Router.m

#import "XFRouter.h"
#import <objc/runtime.h>
#import <objc/message.h>

@implementation XFRouter
#pragma mark - public methods

+ (instancetype)sharedInstance {
    static XFRouter *mediator;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mediator = [[XFRouter alloc] init];
    });
    return mediator;
}

+(void)openURL:(NSString *)stringUrl arg:(NSDictionary *)param error:(NSError *)error callBack:(void (^)(void))callBack {
    if (![stringUrl containsString:@"router://"]) {
        NSLog(@"不合法的router");
        return;
    }
    
    NSString *moduleStr = nil;
    NSString *actionStr = nil;
    
    if (!param) {
        NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
        
        NSArray *urlElementsArrary = [stringUrl componentsSeparatedByString:@"//"];
        NSString *pathStr = [urlElementsArrary lastObject];
        
        if ([stringUrl containsString:@"?"]) {
            NSArray *paramsArrary = [stringUrl componentsSeparatedByString:@"?"];
            NSString *paramsStr = [paramsArrary lastObject];
            
            for (NSString *paramStr in [paramsStr componentsSeparatedByString:@"&"]) {
                NSArray *elts = [paramStr componentsSeparatedByString:@"="];
                if([elts count] < 2) continue;
                [params setObject:[elts lastObject] forKey:[elts firstObject]];
            }
            NSArray *pathStrArrary = [pathStr componentsSeparatedByString:@"?"];
            pathStr = [pathStrArrary firstObject];

        }
        param = params;
        
        NSArray *pathElementsArrary = [pathStr componentsSeparatedByString:@"/"];
        moduleStr = [pathElementsArrary firstObject];
        actionStr = [pathElementsArrary lastObject];
    }
else {
        if ([stringUrl containsString:@"?"]) {
            NSLog(@"不合法的router");
            return;
        }
        
        NSArray *urlElementsArrary = [stringUrl componentsSeparatedByString:@"//"];
        NSString *pathStr = [urlElementsArrary lastObject];
        
        NSArray *pathElementsArrary = [pathStr componentsSeparatedByString:@"/"];
        moduleStr = [pathElementsArrary firstObject];
        actionStr = [pathElementsArrary lastObject];
    }
        
    NSString *routerStr = [NSString stringWithFormat:@"routerHandle_%@_%@:callback:",moduleStr,actionStr];
    
    Class targetClass = NSClassFromString(moduleStr);
    SEL action = NSSelectorFromString(routerStr);
    
    ((void(*)(id,SEL,id,id))objc_msgSend)(targetClass, action,param,callBack);
    if (callBack) {
        callBack();
    }
}

//如需增加参数参考以下方法
/*
- (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:&params 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:&params 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:&params 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:&params 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:&params 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

下面,重温一下JDRouter中的过程:

大致实现如下:

实现一个JDRouter 的组件,用来实现组件与组件之间的通信。
注意:JDRouter只暴露一个头文件,其中两个方法:输入,输出(宏规范),以统一调用和回调方式。

//// 输入(A 调用B)
router://JDBClass/getString?name=Steven

//// 输出(B 被 A 调用)
+(id)getDataWithString:(NSString *)name {   
    NSString *str = [NSString stringWithFormat:@"HI, %@", name];   
    return str;
}

输入说明>> 通过 JDRouter 调用,类似于有这样一个方法,完成 a 到 b 的通信。

id g = [JDRouter openURL:@"router://JDBClass/getString?name=steven" arg:nil error:nil completion:nil];

输出说明>> module.m中的方法实现,及完成 a 到 b 的回调。使用宏替换,在 JDRouter 里提供一个输出的规范,这样在其他Module的.m中直接写一个类方法可以让 JDRouter 通过 URI 里的内容可以映射过去,输入/输出方式就得到了统一。

#define JDROUTER_EXTERN_METHOD(m,i,p,c) + (id) routerHandle_##m##_##i:(NSDictionary*)arg callback:(Completion)callback

将上面的类方法变为宏替换输出:

JDROUTER_EXTERN_METHOD(JDBClass, eat, arg, callback) {   
    NSString *name = arg[@"name"];
    NSString *str = [NSString stringWithFormat:@"HI, %@", name];   
    return str;
}
上一篇 下一篇

猜你喜欢

热点阅读