- (void)_registerRoute:(JLRRouteDefinition *)route { //如果是最高优先级或者之前都没注册过就直接添加 if (route.priority == 0 || self.mutableRoutes.count == 0) { [self.mutableRoutes addObject:route]; } else { NSUInteger index = 0; BOOL addedRoute = NO; // 按照优先级进行排序 // search through existing routes looking for a lower priority route than this one for (JLRRouteDefinition *existingRoute in [self.mutableRoutes copy]) { if (existingRoute.priority < route.priority) { // if found, add the route after it [self.mutableRoutes 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.mutableRoutes addObject:route]; } } [route didBecomeRegisteredForScheme:self.scheme]; }
- (instancetype)initWithURL:(NSURL *)URL options:(JLRRouteRequestOptions)options additionalParameters:(nullableNSDictionary *)additionalParameters { if ((self = [super init])) { self.URL = URL; // URL self.options = options; // options self.additionalParameters = additionalParameters; // 额外参数 BOOL treatsHostAsPathComponent = ((options & JLRRouteRequestOptionTreatHostAsPathComponent) == JLRRouteRequestOptionTreatHostAsPathComponent); //取出路径的各个部分 NSURLComponents *components = [NSURLComponents componentsWithString:[self.URL absoluteString]]; //是否将host作为path if (components.host.length > 0 && (treatsHostAsPathComponent || (![components.host isEqualToString:@"localhost"] && [components.host rangeOfString:@"."].location == NSNotFound))) { // convert the host to "/" so that the host is considered a path component NSString *host = [components.percentEncodedHost copy]; components.host = @"/"; components.percentEncodedPath = [host stringByAppendingPathComponent:(components.percentEncodedPath ?: @"")]; } //格式化后的路径 NSString *path = [components percentEncodedPath]; // handle fragment if needed if (components.fragment != nil) { BOOL fragmentContainsQueryParams = NO; NSURLComponents *fragmentComponents = [NSURLComponents componentsWithString:components.percentEncodedFragment]; if (fragmentComponents.query == nil && fragmentComponents.path != nil) { fragmentComponents.query = fragmentComponents.path; } if (fragmentComponents.queryItems.count > 0) { // determine if this fragment is only valid query params and nothing else fragmentContainsQueryParams = fragmentComponents.queryItems.firstObject.value.length > 0; } if (fragmentContainsQueryParams) { // include fragment query params in with the standard set components.queryItems = [(components.queryItems ?: @[]) arrayByAddingObjectsFromArray:fragmentComponents.queryItems]; } if (fragmentComponents.path != nil && (!fragmentContainsQueryParams || ![fragmentComponents.path isEqualToString:fragmentComponents.query])) { // handle fragment by include fragment path as part of the main path path = [path stringByAppendingString:[NSString stringWithFormat:@"#%@", fragmentComponents.percentEncodedPath]]; } } // strip off leading slash so that we don't have an empty first path component if (path.length > 0 && [path characterAtIndex:0] == '/') { path = [path substringFromIndex:1]; } // strip off trailing slash for the same reason if (path.length > 0 && [path characterAtIndex:path.length - 1] == '/') { path = [path substringToIndex:path.length - 1]; } // split apart into path components self.pathComponents = [path componentsSeparatedByString:@"/"]; // convert query items into a dictionary NSArray <NSURLQueryItem *> *queryItems = [components queryItems] ?: @[]; NSMutableDictionary *queryParams = [NSMutableDictionary dictionary]; for (NSURLQueryItem *item in queryItems) { if (item.value == nil) { continue; } if (queryParams[item.name] == nil) { // first time seeing a param with this name, set it queryParams[item.name] = item.value; } elseif ([queryParams[item.name] isKindOfClass:[NSArrayclass]]) { // already an array of these items, append it NSArray *values = (NSArray *)(queryParams[item.name]); queryParams[item.name] = [values arrayByAddingObject:item.value]; } else { // existing non-array value for this key, create an array id existingValue = queryParams[item.name]; queryParams[item.name] = @[existingValue, item.value]; } } self.queryParams = [queryParams copy]; } returnself; }
@interfaceJLRRouteRequest : NSObject
/// The URL being routed. 需要被路由的URL @property (nonatomic, copy, readonly) NSURL *URL; /// The URL's path components. URL的路径部分 @property (nonatomic, strong, readonly) NSArray *pathComponents; /// The URL's query parameters. URL的请求参数部分 @property (nonatomic, strong, readonly) NSDictionary *queryParams; /// Route request options, generally configured from the framework global options. 请求的可选部分 @property (nonatomic, assign, readonly) JLRRouteRequestOptions options; /// Additional parameters to pass through as part of the match parameters dictionary. 额外参数 @property (nonatomic, copy, nullable, readonly) NSDictionary *additionalParameters;
- (NSDictionary <NSString *, NSString *> *)routeVariablesForRequest:(JLRRouteRequest *)request { NSMutableDictionary *routeVariables = [NSMutableDictionary dictionary]; BOOL isMatch = YES; NSUInteger index = 0; for (NSString *patternComponent inself.patternPathComponents /*这里存着变量名*/) { NSString *URLComponent = nil; //是否是通配符 BOOL isPatternComponentWildcard = [patternComponent isEqualToString:@"*"]; // 1. 取出request中同样位置的pathComponents if (index < [request.pathComponents count]) { URLComponent = request.pathComponents[index]/*请求这里存着变量值*/; } elseif (!isPatternComponentWildcard) { // 请求路径参数小于或者等于的时候还包含有通配符是不可能的 // URLComponent is not a wildcard and index is >= request.pathComponents.count, so bail isMatch = NO; break; } //从patternPathComponent看当前的path是否是带参数的path,带参数的path之前都是 :xxx这种以冒号开头 if ([patternComponent hasPrefix:@":"]) { // this is a variable, set it in the params NSAssert(URLComponent != nil, @"URLComponent cannot be nil"); // 获取变量名---> 将开头的冒号以及结尾的#去掉 NSString *variableName = [self routeVariableNameForValue:patternComponent]; // 变量值 ---> 将结尾的#去掉 NSString *variableValue = [self routeVariableValueForValue:URLComponent]; // Consult the parsing utilities as well to do any other standard variable transformations BOOL decodePlusSymbols = ((request.options & JLRRouteRequestOptionDecodePlusSymbols) == JLRRouteRequestOptionDecodePlusSymbols); variableValue = [JLRParsingUtilities variableValueFrom:variableValue decodePlusSymbols:decodePlusSymbols]; //将变量名为key,值为value 存在 routeVariables中 routeVariables[variableName] = variableValue; } elseif (isPatternComponentWildcard /*包含通配符的情况*/) { // match wildcards NSUInteger minRequiredParams = index; if (request.pathComponents.count >= minRequiredParams) { // match: /a/b/c/* has to be matched by at least /a/b/c // 将通配的部分统一放到routeVariables[JLRouteWildcardComponentsKey]中 routeVariables[JLRouteWildcardComponentsKey] = [request.pathComponents subarrayWithRange:NSMakeRange(index, request.pathComponents.count - index)]; isMatch = YES; } else { // not a match: /a/b/c/* cannot be matched by URL /a/b/ isMatch = NO; } break; } elseif (![patternComponent isEqualToString:URLComponent]) { // break if this is a static component and it isn't a match isMatch = NO; break; } index++; } if (!isMatch) { // Return nil to indicate that there was not a match routeVariables = nil; } return [routeVariables copy]; }
- (NSDictionary *)matchParametersForRequest:(JLRRouteRequest *)request routeVariables:(NSDictionary <NSString *, NSString *> *)routeVariables { NSMutableDictionary *matchParams = [NSMutableDictionary dictionary]; // Add the parsed query parameters ('?a=b&c=d'). Also includes fragment. // 添加query参数 BOOL decodePlusSymbols = ((request.options & JLRRouteRequestOptionDecodePlusSymbols) == JLRRouteRequestOptionDecodePlusSymbols); [matchParams addEntriesFromDictionary:[JLRParsingUtilities queryParams:request.queryParams decodePlusSymbols:decodePlusSymbols]]; // Add the actual parsed route variables (the items in the route prefixed with ':'). // 添加路径中的参数 [matchParams addEntriesFromDictionary:routeVariables]; // Add the additional parameters, if any were specified in the request. // 添加request中附带的额外的参数 if (request.additionalParameters != nil) { [matchParams addEntriesFromDictionary:request.additionalParameters]; } // Finally, add the base parameters. This is done last so that these cannot be overriden by using the same key in your route or query. // 添加request.URL ,self.scheme这些公共参数 [matchParams addEntriesFromDictionary:[self defaultMatchParametersForRequest:request]]; return [matchParams copy]; }