介绍:
万能API路由用于iOS框架中,通过指针参数、KVC、AOP等编程思想,解决程序中的耦合问题, 实现类、实例方法的任意调用。使用指针入参,支持基本数据类型、OC对象类型、结构体、block块、代理等;通过KVC来实现属性的动态赋值操作,实现大型App任意模块之间的解耦。尤其是解决依赖pod管理开发的大型组件相互依赖,和不同sdk底层依赖相同库产生的冲突问题,使得各个sdk完全解耦。
万能路由API功能:
1.万能API路由利用AOP编程思想、指针参数等来实现任意object-c 的API调用。支持基本数据类型、对象类型、代理、block、枚举、结构体等(系统自带和自定义均支持)
2.本sdk用于组件化开发模式,多sdk嵌套等复杂的情况,完全解决项目开发的耦合度。
组件已开源,地址:https://github.com/Tkoul/TKRouter
pod引入 : pod'TKRouter'
如果找不到,删除本地pod缓存索引,更新下spec库即可。
设计背景:
组件开发模式:很多庞大项目开发,是有多个sdk同时开发。主App就是一个容器,各个组件开发完毕,然后进行组合,装在这个容器上。高内聚低耦合,符合软件设计的标准,完美。
当然组件化开发并不是这么肤浅,还有组件化开发的好处这里都不做赘述。网上有很多资料,大的项目离不开组件化的思想。
组件化路由:这样的开发模式,效率大大提升,人员分工明确,各个团队之间的开发有条不紊。这样在各个团队之间的桥梁--路由的作用就不言而喻。常规的路由,包括目前流行的路由三方组件都有自己的定义规则,一个项目突然适配起来不是那么简单。
开发问题:项目主App,包含多个组件,比如其中一个组件A,一个组件B,分别有俩个团队负责开发,都有自己完整的流程,那么两个库都依赖了AFNetworking 这个请求第三方(大公司会封装自己的请求框架)。这里依照afnet为例
A单独开发:A项目,引入 'AFNetworking', '~> 3.0' 然后 高高兴兴去开发了,在项目期限结束的时候,验证、提交、完事。
B单独开发:B为老组件,很早就引入 'AFNetworking', '~> 2.0' 然后 高高兴兴去开发了,在项目期限结束的时候,验证、提交完事。
1.但是当两个组件进行组合的时候就出现问题了,因为两个版本不兼容。
可能会有以下两种解决思路:
方案一:立即联系B,告知版本库升级到3.0用新的,告知所有的团队以某个版本为准。
方案二:把AFNetworking作为主App的直接bundel内容,其它组件去掉AFNetworking
对于方案一:首先如果有100个组件都包含了AFNetworking,50个提供源码,50个提供的是静态库。那么最后合到主app,就会有51个AFNetworking,这是无法忍受,不可原谅的。如果再有某些大神,静态库打包的时候不给人家加个前缀,合并到主app,就会有很多类似AFNetworking.o这种重复名称的文件,导致无法运行。
对于方案二:提供源码的组件,开发的时候可以导入,提交的时候屏蔽AFNetworking,由于头文件包含了项目中没有的库会提交不上去(此种是使用pod的情况下,如果直接提供源码到主App的,请忽略这条)。提供静态库的话,如果使用了,但是不包含AFNetworking是打不出包的,更谈不上上传了。
以上问题对组件化开发App造成很大的困扰,为了解决这个问题,大牛们早就前行在道路上,就出现了“组件二进制化||平滑二进制组件”的实现,本人也在使用。原理就是把第三方共用组件比如AFNetworking先做成二进制文件,大家都依赖二进制文件来做到统一。能极大解决问题,弊端就是需要维护很多二进制文件,并且每次升级不同团队都要做二进制文件的md5哈希取值,这个值一样才能说明是同一个。每次代码升级开发完毕,必须把源码重新生成新的二进制,维护成本很高,很多时候会有遗漏。
TKRouter使用:
针对诸多的问题,我就开发了TKRouter。
常规实现:
下面是我的某个组件调用AFNetworking的网络检测代码:
1. 项目依赖 AFNetworking
2. 实现的地方引入 AFNetworkReachabilityManager.h
3. 实现如下代码
AFNetworkReachabilityManager *maneage=[AFNetworkReachabilityManager sharedManager];
[maneage startMonitoring];
[maneage setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
if (status == 0) {
//无网络链接
} else {
//有网链接
}
}];
TKRouter 实现:
直接上代码 ,不要依赖,不需要引入三方的头文件:
ReturnStruct managerStur = [[[TKRouter router] routerClassName:@"AFNetworkReachabilityManager"] classMethodSelect:@"sharedManager" parameter:nil, nil];
NSObject *objManager = managerStur.returnValue;
[objManager instanceMethodSelect:@"startMonitoring" parameter:nil, nil];
void (^ReachabilityStatusChangeBlock)(NSInteger status) = ^(NSInteger status){
if (status == 0) {
//无网络链接
} else {
//有网链接
}
};
[objManager instanceMethodSelect:@"setReachabilityStatusChangeBlock:" parameter:&ReachabilityStatusChangeBlock, nil];
TKRouter优点:
1.调用原子api ,使用简单易懂,就像调用方法一样。
2.多组件开发,很多组件都依赖某些第三方组件的时候,例如AFNetworking,那么我们就不用考虑依赖的事情了 甚至不用去维护它,直接路由调用。三方头文件都不需要引入,想用的时候直接使用即可。
TKRouter是处理主App或者组件中已经存在了第三方,其他组件在开发的时候就能通过TKRouter调用该三方的工具。其他组件开发的时候,demo工程需要导入AFNetworking,但是打包和上传的时候不用包含AFNetworking,不会有报错、打不出包、上传不了的问题。依然可与执行pod lib lint 和 pod repo push,推送的是组件。想达到实际效果就在demo工程手动导入一下AFNetworking即可。
注意事项:TKRouter 强大的原子调用毋庸置疑,三方sdk一旦开源!api就固定了,所以主工程种几百个组件有一个AFNetworking就够了,因为AFNetworking的api一般不会随着版本升级而变更,只会里面的实现会有所变更。某个开源库万不得已改变了api! 那么由于我们用的路由aop思想编写出来的程序,api变化,我们组件并没有报错,红点提示。所以每次开源库大版本升级关注下api变化即可。一般很少遇到!再说,一般都会迭代几个版本才会慢慢抛弃一些方法。不要告诉我,你懒到自己代码写一遍,一万年都不再维护吧。咱们不用关系他升级到第几个版本,让拥有这个三方的组件更新维护版本,而我们只关注api!
调用注意:TKRouter所有的方法参数为指针类型!就牵涉到左值右值方面的知识。我传的是左值-内存中有实际内存的。有兴趣的小伙伴可以研究下。简单起见,如果是方法传来的值,直接使用会报红,我们定义一个它的对象,接收一下值,用新定义的对象取地址即可。左值、右值:https://www.jianshu.com/p/8095517dbb3f
核心方法:
一:
/**
实例子对象 给属性 赋值
@param propertyParameter 属性字典组合。 key为属性的字符串。 value为属性需要赋予的值
@return 返回
*/
- (ReturnStruct)setPropertyParameter:(NSDictionary*)propertyParameter;
二:
/**
执行实例方法
@param selectString 方法名
@param methodParaments 方法参数 --- 指针参数 对应参数 以指针类型传入
@return 返回值
*/
- (ReturnStruct)instanceMethodSelect:(NSString*)selectString parameter:(void *)methodParaments, ... NS_REQUIRES_NIL_TERMINATION;
三:
/**
执行类方法
@param selectString 方法名
@param methodParaments 方法参数 --- 指针参数 对应参数 以指针类型传入
@return 返回值
*/
+ (ReturnStruct)classMethodSelect:(NSString*)selectString parameter:(void*)methodParaments, ... NS_REQUIRES_NIL_TERMINATION;
示例:
比如在demo工程:
调用方法:
+ (NSString*)TestMethodOneWithObjectType:(NSDictionary*)dic andPar:(NSString*)strOne andPar:(NSNumber*)numTwo;
//step 二: AOP 调用+ 方法。即类方法
Class TKMethodTestClass = [[TKRouter router] routerClassName:@"TKMethodTest"];
NSDictionary *par1 =@{@"key1":@"Hello:TKRouter",@"key2":@"SayHi"};
NSString *par2 = @"字符串类型参数";
NSNumber *par3 = [NSNumber numberWithInt:1688];
[TKMethodTestClass classMethodSelect:@"TestMethodOneWithObjectType:andPar:andPar:" parameter:&par1,&par2,&par3, nil];
调用方法:- (void)TestMethodTwoWithBasicType:(int)numberone andSecond:(CGFloat)floatTwo;
//step 四-: 实例对象调用- 方法 - 传入基本数据类型
NSObject *object = [[TKRouter router] routerGetInstanceWithClassName:@"TKMethodTest"];
int par4 = 8888;
CGFloat par5 = 88.88;
ReturnStruct returnOne = [object instanceMethodSelect:@"TestMethodTwoWithBasicType:andSecond:"parameter:&par4,&par5, nil];
调用方法:- (BOOL)TestMethodTwoWithStructType:(CGRect)struOne and:(CGSize)struTwo;
//step 四二: 实例对象调用- 方法 - 传入结构体
NSObject *object = [[TKRouter router]routerGetInstanceWithClassName:@"TKMethodTest"];
CGRect par6 = CGRectMake(100, 100, 200.5, 200);
CGSize par7 = CGSizeMake(666.0, 888.5);
ReturnStruct returnTwo = [object instanceMethodSelect:@"TestMethodTwoWithStructType:and:" parameter:&par6,&par7, nil];
调用方法:- (NSString*)TestMethodTwoWithBlockType:(void(^)(NSString *str1))block;
//step 四三: 实例对象调用- 方法 - 传入block块
NSObject *object = [[TKRouter router]routerGetInstanceWithClassName:@"TKMethodTest"];
void(^parblock8)(NSString *str1) = ^(NSString *str1){
NSLog(@"\n block 得到的值回调。==%@\n\n\n",str1);
};
ReturnStruct returnThere = [object instanceMethodSelect:@"TestMethodTwoWithBlockType:" parameter:&parblock8, nil];
以上是基本的调用,更多的去github上download下来可自行查看。(地址:https://github.com/Tkoul/TKRouter)
比如:初始化ctrl,TKRouter给ctrl赋值,TKRouter调用push,推出新的视图,TKRouter设置代理!实现基本数据类型,结构体,oc类型,block块,代理等!
如果有帮到你,请去俺的https://github.com/Tkoul/TKRouter地址上 star一下。
欢迎大家交流,email:[email protected]