iOS Router原理篇

前言:

iOS强调模块化、组件化,讲究模块内高内聚,模块间低耦合。
那么模块与模块之间的低耦合,就要求模块间的通信要尽可能的减少依赖,SDRouter,就是这样的中间产物。

SDRouter的设计是参照一水流年的想法来实现的,总体和他的想法保持一致,但是会更加的简洁、易用,作者设计的拓展性强,但是依赖过多,导致库不太容易单独存在。那么,你可以看到使用该库,可以使用URL的方式打开任何Native页面,如下面一个伪协议:asone://oneController?title=as_one
asone是AppSchema.完全按照http协议涉及的跳转,让该库SDRouter的结构简单,想法也简单。

先设想一下:如何从AController以URL的方式跳转到BController并携带参数?

先看下SDRouter中是怎么实现的:

- (void)viewDidLoad {
    [super viewDidLoad];
//  在页面上添加一个button
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 100, 80)];
    [button setTitle:@"one" forState:UIControlStateNormal];
    [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [button addTarget:self action:@selector(go) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}

//  点击跳转到下一个控制器 并携带下一个控制器的标题
- (void)go {
    NSDictionary *param = @{@"title":@"as_one"};
    NSURL *url = SDURLRouteQueryLink(OneController, param);
    [[SDRouter shareRutor] rutor:url];
}

再设想一下:如何从BController以OpenUrl的方式打开CController并传递参数?

//  先忽略+load方法中的内容
+ (void)load {
    [[SDRouter shareRutor] addPaten:OneController callback:^(SDRouterContext *context) {
        NSLog(@"优品财富: %@",context.paramters);
        OneViewController *textOneVc = [[OneViewController alloc] init];
        textOneVc.navigationItem.title = context.paramters[@"title"];
        [context.topNavigationController pushViewController:textOneVc animated:YES];
    }];
}

//  先看这里
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor colorWithRed:((float)arc4random_uniform(256) / 255.0) green:((float)arc4random_uniform(256) / 255.0) blue:((float)arc4random_uniform(256) / 255.0) alpha:1.0];
    //  页面上添加一个按钮
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 100, 80)];
    [button setTitle:@"openUrl" forState:UIControlStateNormal];
    [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [button addTarget:self action:@selector(openUrl) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
    
}
//  点击调转,以openUrl的方式打开一个控制器并携带参数,该参数是navigationTitle。
- (void)openUrl {
    NSDictionary *dict = @{@"title":@"as_two"};
    NSURL *url = SDURLRouteQueryLink(TwoController, dict);
    [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) {}];
}

最后再想一下,H5怎么快捷的打开CController并携带参数?

//  H5页面添加一个按钮并添加js跳转

function jump(){
    window.location="asone://threeController?title=asthree";
}

上面三种设想可以参看下图:

hello1.gif

SDRouter设计思想:

  • URL部分:看似URL跳转,实际上是对URL的解析、组合编解码。
    SDRouterUtil主要包含了对URL的解析及组合编解码的方法。
@interface NSString (SDURLEncode)
- (NSString *)URLEncode;
- (NSString *)URLDecode;
@end
//  用来处理参数携带 拼接参数 返回编码、拼接后的URL
FOUNDATION_EXTERN NSURL *SDURLRouteQueryLink(NSString *baseUrl, NSDictionary *query);

//  添加参数
FOUNDATION_EXTERN NSString *SDURLRouteJoinParamterString(NSString *urlStr, NSString *query);

//  将拼接好的参数encode
FOUNDATION_EXTERN NSString *SDURLRouteEncodeURLQueryParamters(NSDictionary *paramter);

//  将参数decode
FOUNDATION_EXTERN NSDictionary *SDURLRouteDecodeURLQueryParamters(NSString *urlStr);
  • 跳转:
    URL拼接好,从AController跳转到BController,跳转的方法其实很简单。
    [[SDRouter shareRutor] rutor:url];那这句代码又做了什么呢?重点看这段代码
+ (void)load {
    [[SDRouter shareRutor] addPaten:OneController callback:^(SDRouterContext *context) {
        NSLog(@"优品财富: %@",context.paramters);
        OneViewController *textOneVc = [[OneViewController alloc] init];
        textOneVc.navigationItem.title = context.paramters[@"title"];
        [context.topNavigationController pushViewController:textOneVc animated:YES];
    }];
}

在SDRouter中提供了一个注册的方法,必须在load方法中注册,+load是已知的执行最早的方法,比main函数好要早,这里不做过多解释,不明白可以参看你真的了解+load方法吗?
addPaten:OneController其实这个oneController是定义的字符串常量,如:asone://oneController?title=as_one,在App启动后,SDRouter会记录当前这个控制器和所携带的callback

//  实现比较简单
- (void)addPaten:(NSString *)paten callback:(SDCompleteCallback)callback{
    NSDictionary *dict = @{paten:callback};
    if (![_results containsObject:dict]) {
        [_results addObject:dict];
    }
}

在调用[[SDRouter shareRutor] rutor:url]的时候,会匹配需要前往的url。

- (void)rutor:(NSURL *)paten {
    SDURLParser *parser = [[SDURLParser alloc] initWithURL:paten];
    [_results enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([[obj allKeys].firstObject isEqualToString:parser.paten]) {
            SDRouterContext *context = [[SDRouterContext alloc] init];
            context.paramters = parser.paramters;
            if ([obj allValues].firstObject) {
                SDCompleteCallback callback = (SDCompleteCallback)[obj allValues].firstObject;
                callback(context);
            }
            *stop = YES;
        }
    }];
}
  • push
    到这里,已经能够保证在+load方法中获取到参数,重点是页面怎么push出来。因为+load方法执行时,是不能够获取到navigationController的,那么怎么获取到并push呢?

  • 全局UI栈
    其实一开始并没有该类,但是通过其他方式获取总是觉的鸡肋,还是通过一水文章的点拨才有了该类。
    该类中利用runtime方法替换,重写Controller的生命周期方法,声明一个指针数组,在viewWillApear的时候加入UI栈,在disWill的时候移除UI栈,参数的携带和栈顶控制器的携带,是通过SDContext来完成的。该类中包含了参数及navigation。

讲解比较粗略,想了解的可以到demo中查看。
github

你可能感兴趣的:(iOS Router原理篇)