简单易用的iOS路由
最近都在研究iOS的路由,略有心得,当然GitHub上成熟的路由已经很多,但还是自己封装了一个,简单易用易懂的,基本满足项目的要求
关于路由是什么,在这里我就不多说了 https://www.jianshu.com/p/8b6c9d61f249 这一篇解释的很好。
那么进入正题说说这个WMZRouter,这个路由可以实现对象的获取,对象实例方法和类方法的调用,单例方法的调用,网络请求,VC的跳转,获取tableViewCell等。。
首先当然是这个调用:
1 最简单的调用
//调用的URL
id URL = RouterURL().target(@"getReginerVC");
//调用的参数
id param = RouterParam().selfParam(@{});
//调用方法
[[WMZRouter shareInstance] handleWithURL:URL WithParam:param]
2 功能多一点的调用
//调用的URL
id URL = RouterURL().target(@"getReginerVC").action(@"action");
//调用的参数(有带权限,跳转,日志,缓存,错误处理,带要传递过去的属性)
id param = RouterParam().permissions(PermissionTypePhoto, ^(id anyID, BOOL success)
{
NSLog(@"%@",anyID);
}).enterVCStyle(RouterPush).log(@"日志").cache(^(id anyID, BOOL success) {
NSLog(@"缓存");
}).cacheTime(10).error(RouterPushFail).selfParam(@{});
//调用方法
[[WMZRouter shareInstance] handleWithURL:URL WithParam:param WithHandBlock:^(id anyID, BOOL success) {
}];
那么开始讲解这个Router的原理
首先要成为这个路由可以操控的对象需要实现协议
// 协议 (要成为handle必须实现该协议和协议的方法)
@protocol WMZRouterProtocol <NSObject>
@required
//调用时的target要与此实现方法的返回值相同)
+ (NSString*)routerPath;
这个协议中返回的字符串与调用的URL中的target要相同 例如id URL = RouterURL().target(@"getReginerVC");
中调用协议的类的routerPath方法就要返回getReginerVC
WMZRequest类是传入的路由处理对象
它处理了传入的URL 取出对应的scheme,target,action等
那么我们来看看WMZRouter类具体是如何实现的
首先第一次使用路由的时候我们要获取项目中的所有实现协议的类把他加入handle
要借助强大的runtime
//获取当前app的类库
NSString *app_img = [NSBundle mainBundle].executablePath;
//获取类库下的所有类
const char **classes = objc_copyClassNamesForImage([app_img UTF8String], &cls_count);
for ( unsigned int i = 0 ; i < cls_count ; ++ i ) {
//判断是否实现协议和实现协议的routerPath 才存入存放handle的字典 具体实现看demo
。。。。。
[self.handleDic setObject:cls forKey:target];
}
注册完则处理handle
首先判断调用的是方法还是对象
//调用的是实例方法或者类方法
if (WMZBoolNill(myURL.Expression)&&WMZBoolNill(myURL.target)) {
[self performSeleectWithClass:className withURL:myURL];
return;
}
//只调用对象 if (WMZBoolNill(myURL.target)) {
myURL.returnValue = [self getClass:myURL WithClass:className];
}
(1) 调用对象 调用的是对象的话 我们要创建对象并判断有没有携带属性有则赋值给它(具体实现看demo)
// 创建对象 id instance = [[myClassName alloc] init];
//获取对象的所有属性 objc_property_t * properties = class_copyPropertyList([instance class], &outCount);
//遍历所有属性有存在和传入的属性一致的则赋值 [instance setValue:obj forKey:key];
(2) 调用的是方法 难点在于系统的调用方法返回只有返回id 我们需要返回BOOL值 int值等 那么我们就要去修改返回类型(在WMZRouterbase基类里) 主要借助NSInvocation改变
/* * 调用方法修改返回值
* @param action 方法名
* @param URL 路由对象
* @param target target
* @param dic 所带参数 * returm id 返回值 */
+ (id)performIDSelector:(SEL)action withObjects:(WMZRequest*)URL Tagert:(id)target withParm:(id)dic;
在这里我们已经实现对类方法和对象的基本调用了,那么我们如果需要实现控制器之间的跳转呢
首先这是对于路由的一种扩展,我们不能去修改WMZRouter类
那么我们要如何在调用路由的方法后调用控制器跳转的方法,我们可以在调用路由的方法后面加入一个控制器跳转的方法,用(AOP)面向切面编程思想 在GitHub上有一个封装好的类(Aspects),原理是什么的可以自行去了解一下 (拦截器在WMZBaseAop类里)
[WMZRouter aspect_hookSelector:@selector(handleWithURL:WithParam:WithHandBlock:) withOptions:AspectPositionAfter|AspectOptionAutomaticRemoval usingBlock:^(id<AspectInfo> aspectInfo)
{
WMZRouter *route = aspectInfo.instance;
NSLog(@"跳转开始");
[WMZRouter expandHandleActionWithID:route.returnURL.returnValue WithRequest:route.returnURL];
} error:NULL];
我们在WMZRouter加入后拦截器 那么我们要怎么让他和调用的实例挂钩呢 我们可以有两种思路
1 决定拦截器要给谁调用,可以用过协议实现
2 调用者可以在调用的时候决定不用哪些拦截器
这个项目里我用的是第二种思路
//调用参数 带enterVCStyle即表示处理控制器的跳转 这样就和实例实现类没有关联了 通过在调用路由的时候添加拦截器实现对应的方法
id param = RouterParam().enterVCStyle(RouterRoot);
其他的对路由的拓展则都是和加入VC跳转相同的思路,具体看demo
好了 这样我们就基本可以实现一个路由了。第一次写文章有什么写的不对的请多多指教。
demo下载地址 WMZRouter