美文网首页
2020-04-23

2020-04-23

作者: 六月的夜 | 来源:发表于2020-04-23 11:43 被阅读0次

    JLRoutes的另类使用及解析

    一、简介

    JLRoutes是一个基于块的API的URL路由库。 它旨在使您以最少的代码轻松处理应用程序中的复杂URL方案。

    通过URL schemes可以实现APP内部,Web和APP之间,以及APP和APP之间页面的跳转。

    二、原理

    JLRoutes是通过解析URL不同的参数,并用block回调的方式处理页面间的传值以及跳转。其本质就是在程序中注册一个全局的字典,key是URL scheme,value是一个参数为字典的block回调。

    三、使用

    在我自己的项目中使用的JLRoutes是比较老的版本,可能是1.6版本左右,功能比较简单,只用JLRoutes这一个类。在使用的过程中为了更加方便因此把routeURL:的返回改为id (之前为BOOL),因此在每个路由block里返回任意非nil对象,表示不再继续走下一个路由 (之前返回YES则不再往下走),这就是本文的另类之处。

    /// Routes a URL, calling handler blocks (for patterns that match URL) until one returns YES, optionally specifying add'l parameters
    + (id)routeURL:(NSURL *)URL;
    + (id)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters;
    
    - (id)routeURL:(NSURL *)URL; // instance method
    - (id)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters; // instance method
    

    做一个简单的跳转场景:A页面跳转到B页面
    首先在B页面的load方法中使用addRoute:handler方法注册B页面的路由

    + (void)load
    {
        [JLRoutes addRoute:TZ_SCENESA handler:^id(NSDictionary *parameters) {
            TZViewControllerA *vc = [[TZViewControllerA alloc] init];
            return vc;
        }];
    }
    

    其中TZ_SCENESA为B页面的路由宏
    注册完以后,会将注册的路由存放在一个全局字典中,只需要在跳转的过程调用对应页面的路由即可。
    为了方便我简单的封装了一个工具类TZRouter,来处理路由的跳转

    [[TZRouter sharedInstance] openURL:TZ_SCENESA params:nil];
    

    其中TZ_SCENESA为B页面的路由地址,params为这个页面是所需要传递的参数
    这样一个简单的跳转就实现了。

    四、源码分析

    4.1 路由注册

    - (void)addRoute:(NSString *)routePattern priority:(NSUInteger)priority handler:(id (^)(NSDictionary *parameters))handlerBlock {
        _JLRoute *route = [[_JLRoute alloc] init];
        route.pattern = routePattern;
        route.priority = priority;
        route.block = [handlerBlock copy];
        route.parentRoutesController = self;
        
        if (!route.block) {
            route.block = [^id (NSDictionary *params) {
                return [NSNumber numberWithBool:YES];
            } copy];
        }
        if (priority == 0 || self.routes.count == 0) {
            [self.routes addObject:route];
        } else {
            NSArray *existingRoutes = self.routes;
            NSUInteger index = 0;
            BOOL addedRoute = NO;
            
            // search through existing routes looking for a lower priority route than this one
            for (_JLRoute *existingRoute in existingRoutes) {
                if (existingRoute.priority < priority) {
                    // if found, add the route after it
                    [self.routes insertObject:route atIndex:index];
                    addedRoute = YES;
                    break;
                }
                index++;
            }
            // if we weren't able to find a lower priority route, this is the new lowest priority route (or same priority as self.routes.lastObject) and should just be added
            if (!addedRoute)
                [self.routes addObject:route];
        }
    }
    

    routePattern:路由
    priority:优先级
    handlerBlock:要处理的回调方法
    该方法生成一个_JLRoute类型的实例对象,改对象记录了注册页面的路由、优先级、回调。并根据优先级将路由添加到全局数组中去。

    4.2 路由解析

    - (NSDictionary *)parametersForURL:(NSURL *)URL components:(NSArray *)URLComponents {
        NSDictionary *routeParameters = nil;
        
        if (!self.patternPathComponents) {
            self.patternPathComponents = [[self.pattern pathComponents] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"NOT SELF like '/'"]];
        }
        // do a quick component count check to quickly eliminate incorrect patterns
        BOOL componentCountEqual = self.patternPathComponents.count == URLComponents.count;
        BOOL routeContainsWildcard = !NSEqualRanges([self.pattern rangeOfString:@"*"], NSMakeRange(NSNotFound, 0));
        if (componentCountEqual || routeContainsWildcard) {
            // now that we've identified a possible match, move component by component to check if it's a match
            NSUInteger componentIndex = 0;
            NSMutableDictionary *variables = [NSMutableDictionary dictionary];
            BOOL isMatch = YES;
            
            for (NSString *patternComponent in self.patternPathComponents) {
                NSString *URLComponent = nil;
                if (componentIndex < [URLComponents count]) {
                    URLComponent = URLComponents[componentIndex];
                } else if ([patternComponent isEqualToString:@"*"]) { // match /foo by /foo/*
                    URLComponent = [URLComponents lastObject];
                }
                
                if ([patternComponent hasPrefix:@":"]) {
                    // this component is a variable
                    NSString *variableName = [patternComponent substringFromIndex:1];
                    NSString *variableValue = URLComponent;
                    NSString *urlDecodedVariableValue = [variableValue JLRoutes_URLDecodedString];
                    if ([variableName length] > 0 && [urlDecodedVariableValue length] > 0) {
                        variables[variableName] = urlDecodedVariableValue;
                    }
                } else if ([patternComponent isEqualToString:@"*"]) {
                    // match wildcards
                    variables[kJLRouteWildcardComponentsKey] = [URLComponents subarrayWithRange:NSMakeRange(componentIndex, URLComponents.count-componentIndex)];
                    isMatch = YES;
                    break;
                } else if (![patternComponent isEqualToString:URLComponent]) {
                    // a non-variable component did not match, so this route doesn't match up - on to the next one
                    isMatch = NO;
                    break;
                }
                componentIndex++;
            }
            if (isMatch) {
                routeParameters = variables;
            }
        }
        return routeParameters;
    }
    

    该方法通过解析路由和全局数组中的路由进行匹配,找到要跳转页面的路由,从而建立跳转关系

    五、版本比对

    在JLRoutes最新的2.1版本中,JLRoutes由以前的一个JLRoutes的基础上增加了JLRParsingUtilities、JLRRouteDefinition、JLRRouteHandler、JLRRouteRequest、JLRRouteResponse这五个类。

    JLRoutes:作为JLRoutes框架的入口,负责注册URL,管理路由以及分配路由。
        
    JLRRouteDefinition:用来封装注册URL的路由信息,包括URL scheme,route pattern,和priority,并且可以根据request提供相应的response。可以通过继承该类来实现自定义的匹配方式。
        
    JLRRouteRequest:用来封装一个URL的路由请求信息,包括URL、解析后的path components 和 query parameters。
    
    JLRRouteResponse:根据URL匹配路由信息时的response,包含isMatch、parameters 等信息。如果 JLRRouteDefinition匹配URL成功时,就会设置属性isMatch为YES,同时将解析URL后的参数和默认参数、附加参数组合返回
    
    JLRRouteHandler:自定义路由handler,将回调参数处理的逻辑交给自定义类去处理。
    
    JLRParsingUtilities:解析URL参数的工具类。
    

    其中在路由解析部分使用NSURLComponents和NSScanner,极大的提高了匹配的容错率。

    代码实例

    相关文章

      网友评论

          本文标题:2020-04-23

          本文链接:https://www.haomeiwen.com/subject/pwpoihtx.html