HHRouter作为一种URL进行控制器之间跳转的一个第三方库,其代码加起来也不到300行,简单明了。
HHRouter.h
可以看注释
#import <Foundation/Foundation.h>
typedef NS_ENUM (NSInteger, HHRouteType) {
HHRouteTypeNone = 0,
HHRouteTypeViewController = 1,
HHRouteTypeBlock = 2
};
typedef id (^HHRouterBlock)(NSDictionary *params);
@interface HHRouter : NSObject
//单例
+ (instancetype)shared;
//注册控制器与url
- (void)map:(NSString *)route toControllerClass:(Class)controllerClass;
//过期了,就是下面这个
- (UIViewController *)match:(NSString *)route __attribute__((deprecated));
//根据路由返回控制器
- (UIViewController *)matchController:(NSString *)route;
//注册block与url
- (void)map:(NSString *)route toBlock:(HHRouterBlock)block;
//根据路由返回block
- (HHRouterBlock)matchBlock:(NSString *)route;
//执行block
- (id)callBlock:(NSString *)route;
//是哪种路由
- (HHRouteType)canRoute:(NSString *)route;
@end
//控制器一个分类,关联一个字典属性
@interface UIViewController (HHRouter)
@property (nonatomic, strong) NSDictionary *params;
@end
HHRouter.m
我先注册[[HHRouter shared] map:@"/user/:userId/" toControllerClass:[UserViewController class]];
- (void)map:(NSString *)route toBlock:(HHRouterBlock)block
{
NSMutableDictionary *subRoutes = [self subRoutesToRoute:route];
subRoutes[@"_"] = [block copy];
}
- (NSMutableDictionary *)subRoutesToRoute:(NSString *)route
{
//@[user,
//:userId]
NSArray *pathComponents = [self pathComponentsFromRoute:route];
NSInteger index = 0;
NSMutableDictionary *subRoutes = self.routes;
/**
这里面赋值比较有意思,没有这个key的时候,增加一个key,value为一个新的字典,在将原来字典指针指向新的字典,循环执行
{
user = {
":userId" = {
"_" = UserViewController;
};
};
}
*/
while (index < pathComponents.count) {
NSString *pathComponent = pathComponents[index];
if (![subRoutes objectForKey:pathComponent]) {
subRoutes[pathComponent] = [[NSMutableDictionary alloc] init];
}
subRoutes = subRoutes[pathComponent];
index++;
}
return subRoutes;
}
- (NSArray *)pathComponentsFromRoute:(NSString *)route
{
NSMutableArray *pathComponents = [NSMutableArray array];
///user/:userId/
NSURL *url = [NSURL URLWithString:[route stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
for (NSString *pathComponent in url.path.pathComponents) {
if ([pathComponent isEqualToString:@"/"]) continue;
if ([[pathComponent substringToIndex:1] isEqualToString:@"?"]) break;
[pathComponents addObject:[pathComponent stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
return [pathComponents copy];
}
匹配控制器UIViewController *viewController = [[HHRouter shared] matchController:@"/user/1/"];
- (UIViewController *)matchController:(NSString *)route
{
NSDictionary *params = [self paramsInRoute:route];
Class controllerClass = params[@"controller_class"];
UIViewController *viewController = [[controllerClass alloc] init];
if ([viewController respondsToSelector:@selector(setParams:)]) {
[viewController performSelector:@selector(setParams:)
withObject:[params copy]];
}
return viewController;
}
- (NSDictionary *)paramsInRoute:(NSString *)route
{
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"route"] = [self stringFromFilterAppUrlScheme:route];
NSMutableDictionary *subRoutes = self.routes;
NSArray *pathComponents = [self pathComponentsFromRoute:params[@"route"]];
for (NSString *pathComponent in pathComponents) {
BOOL found = NO;
NSArray *subRoutesKeys = subRoutes.allKeys;
for (NSString *key in subRoutesKeys) {
if ([subRoutesKeys containsObject:pathComponent]) {
found = YES;
subRoutes = subRoutes[pathComponent];
break;
} else if ([key hasPrefix:@":"]) {
found = YES;
subRoutes = subRoutes[key];
params[[key substringFromIndex:1]] = pathComponent;
break;
}
}
if (!found) {
return nil;
}
}
//这是针对这种url: UIViewController *viewController = [[HHRouter shared] matchController:@"/user/1/?tabIndex=3"];
// Extract Params From Query.
NSRange firstRange = [route rangeOfString:@"?"];
if (firstRange.location != NSNotFound && route.length > firstRange.location + firstRange.length) {
NSString *paramsString = [route substringFromIndex:firstRange.location + firstRange.length];
NSArray *paramStringArr = [paramsString componentsSeparatedByString:@"&"];
for (NSString *paramString in paramStringArr) {
NSArray *paramArr = [paramString componentsSeparatedByString:@"="];
if (paramArr.count > 1) {
NSString *key = [paramArr objectAtIndex:0];
NSString *value = [paramArr objectAtIndex:1];
params[key] = value;
}
}
}
Class class = subRoutes[@"_"];
if (class_isMetaClass(object_getClass(class))) {
if ([class isSubclassOfClass:[UIViewController class]]) {
params[@"controller_class"] = subRoutes[@"_"];
} else {
return nil;
}
} else {
if (subRoutes[@"_"]) {
params[@"block"] = [subRoutes[@"_"] copy];
}
}
return [NSDictionary dictionaryWithDictionary:params];
}
- (NSString *)stringFromFilterAppUrlScheme:(NSString *)string
{
//如果系统配置了scheme,就截取后面参数
// filter out the app URL compontents.
for (NSString *appUrlScheme in [self appUrlSchemes]) {
if ([string hasPrefix:[NSString stringWithFormat:@"%@:", appUrlScheme]]) {
return [string substringFromIndex:appUrlScheme.length + 2];
}
}
return string;
}
block原理差不多,使用demo如下
[[HHRouter shared] map:@"aaaa/:hehe" toBlock:^id(NSDictionary *params) {
UserViewController *vc = [UserViewController new];
vc.params = params;
return vc;
}];
HHRouterBlock block = [[HHRouter shared] matchBlock:@"/aaaa/1/"];
UserViewController *blockVc = block(@{});
/**
(lldb) po blockVc.params
{
block = "<__NSGlobalBlock__: 0x10258f288>";
hehe = 1;
route = "/aaaa/1/";
}
*/
这里面也没什么算法之类的,就是注册类,block,在内存中使用字典保存,取值遍历都是使用原生方法,效率也该可以的,就是项目大了需要路由的所有控制器都要提前注册,内存也该也还好,就是一个dictiona。启动速度的如果不是首要路由,也可延后注册。