- 对于网络层的设计,相信大家都不会陌生,最知名当属于AFNetworking。但是我们实际开发过程中网络层的任务不仅仅需要单纯的网络请求,而是需要处理关于请求数据相关的整个一大块,例如:数据缓存、日志、code校对、错误分析等等。我们希望网络层能起到后台与实际业务的桥梁作用,同时网络层不可以染指业务逻辑。考虑低耦合要求,我们往往会把网络层设计成一个独立模块。而我们大名顶顶的AFNetworking并不能实现这一多功能要求,那么我们就需要对AFNetworking二次封装增加新的功能以满足我们所需要的网络层要求。
一、如何设计(个人浅谈)
- 1.封装形式:对AFNetworking封装分为两种形式,block(集约型)和代理(离散型)。所以我们需要对这两种进行选择。
- 2.缓存模块:业务有时候需要一些重要对数据能够缓存下来而不是每次请求网络。所以我们需要一个缓存数据的模块。
- 3.日志系统:对于每次请求我们不可能说每次都能一次性搞定,而是需要跟后台不断调试,包括我们自己调试,这样我们就需要一个日志模块,这个模块能够打印出关于请求的所有信息,成功能够打印出正确数据,失败能够打印具体的失败信息等,我们只需要简单看看日志就能发现问题所在而不是通过LLDB打断点一步一步调试找出错误。
- 4.低偶合性。网络层必须是一个独立的模块,不能够跟业务逻辑纠缠在一起。对于网络层来讲,我只给你业务提供数据,至于你要用数据去干什么,我并不关心。另一方面,网络层必须跟AFNetworking低耦合,一旦哪天我们不想要用AFNetworking可以轻松替换。
- 5:可操控性:很多时候会出现这种情况,我们给的请求参数明显不对,或者后台返回的数据明显不是业务需求数据。如果我们不加以处理,那么就会导致请求错误,并且这个错误数据流入地业务端,业务端首先需要去甄别数据的好坏,其次再加以使用。一旦业务的请求接口过多势必会导致业务层压力增大。对此,我们需要将这些不必要的错误扼杀在网络层, 所以一些时候网络层需要对请求的参数,以及返回的数据进行校对,或者修订。这样可以减少业务层的压力负担,使其能更好的关注业务逻辑。
- 6.简单,易懂的代码:有些简单的请求如果进行一系列分析的话有会导致代码看上去很烦乱,所以我们还需要提供一些简单易懂的调用方法。
- 7.请求控制:一些没有必要的请求我们需要及时去除。例如:切换页面后,请求还在进行,这时候请求已经没有必要了,我们需要停止,防止流量的浪费,同时对于一些麻烦的请求(大数据的上传下载等)还可以提升流畅度。
二、模式缺陷
1.block形式的缺陷
- block不够安全,稍微不注意就形成循环引用,导致对象释放不了。这种循环引用,一旦出现就比较难检查出来。
- block很难追踪,难以维护
- block会延长相关对象的生命周期
- 在多通信时候,block显得不够直观也不易维护
- block效率低,block出栈需要将使用的数据从栈内存拷贝到堆内存
2.block形式的优点
- 使用更简单,易维护,能够直接访问上下文,获取数据方便
- 省去了写代理的很多代码
3.delegate形式的优点
- delegate更安全, delegate 的方法是分离开的,不会引用上下文,不容易循环引用
- 代码的连贯性不是很好,没有 block 好读
- delegate 让多个方法分成一组,只需要设置一次,就可以多次回调。即同一个请求可能会多次调用的时候delegate只需要设置一次,而block需要多次,这种情况下也推荐用delegate形式
- delegate运行成本低,delegate只是保存了一个对象指针,直接回调,没有额外消耗
3.delegate形式的缺点
- 使用繁琐,请求接口数量多的时候delegate要写更多的代码。
- 方法的声明和实现分离开来,代码的连贯性不是很好。
- 需要临时存储很多数据,跟业务交接不方便
三、个人方案
1.业务分析
- 开始公司业务量不多的时候,接口较少,也比较简单。自己用的都是block形式,轻型、简单、代码易读易修改。这样一方面符合刚开始的快速迭代,二方面也节省时间。但是随着后来业务量的增加,业务功能的细致化后发现,自己的业务层开始变的非常臃肿,对于一些接口,业务还需要去自己检测数据的格式符不符合要求。一些相同的请求,多次不同地方调用,block就的在多个地方多次重写等等导致业务层再次变的繁琐...
2.问题处理 - XXHNetwork
- 对于相同接口多次调用等问题我们应该使用delegate模式,但由于之前使用大量的block一时间全部更换不现实,而且一些简单的接口更换后反而显得臃肿,所以最终采用但是混合模式,即网络层最外层即提供block形式接口,同时也提供delegate形式接口,至于需要用哪种形式,可由开发人员根据业务需求自行选择,以达到最优效果。
四、XXHNetwork封装缓存、日志输出、参数,返回数据拦截控制
1.数据缓存
XXHNetwork提供了独立的缓存模块(XHNetworkCacheManager)该模块底层运用的是YYCache,因为YYCache LRU算法比较符合我们现实的使用场景,当然我们系统提供的NSCache也是ok的,看个人喜好吧。缓存模块提供下面功能
- 支持最大缓存数量
- 缓存版本控制、app版本检测
- 根据请求shouldAllIgnoreCache判断是否需要缓存
- 缓存的有效时长
- 内存、磁盘两中存储方式
- 缓存数据增、删、改、查
2.日志输出
日志输出由独立的模块XHNetworkLogManager控制,里面提供各种情况(成功、失败)下日志输出接口,只需要调用初始化方法传入相应数据即可!
- func appendURL:用于打印请求head、body信息
- func logDebugInfo:用于打印请求返回数据信息
当然你也可以根据自己的需求在这个模块增加日志收集接口等等个人业务相关等需求
3.底层接口分离
- XXHNetwork跟底层AF交互的只有XHApiProxy一个类,同时只有一个方法(func callNetwork)这一个方法跟AF交互,其他所有不同方式请求处理都是通过调用该方法,也就是以后如果想替换AFNetworking那么只需要更换这一个方法即可替换非常方便!
- 底层方法callNetwork还增加了一些基本的code处理,对于一些明显的错误,例如token失效等,这里直接放在最底层进行处理这样只需要处理一次即可。
- XHApiProxy提供供外界调用基本请求接口,同时还提供了请求控制接口,可以实现部分或者全部取消请求。
4.上层接口
- block形式:XHNetworkBlockManager,只要实现相应的成功、失败回调即可,相关的错误类型查看XHNetworkErrorType
- delegate形式:XHNetworkDelegateManager,这个可用于参数控制,数据返回检测,只要实现相应的代理即可实现相应功能,详细看注释。
- XHNetworkConfigution:网络配置,网络基本配置在这里修改
- XHNetworkRequestConfig:请求配置,你可以根据业务需求初始化具体的配置,你也可以不用管使用默认的请求配置。
- XHURLResponse:返回数据对象,不管成功还是失败都会返回该对象,该对象包含了所有需要信息,AN返回的基本数据responseObject为(content),error:为错误信息。其他信息请查看注释。
5.使用
- block使用:
XHNetworkBlockManager.shared.request(Method: XHNetworkRequestType.get, APIString: "", Parameters: params, SuccessBlock: { (successResponse) in
print("--------------方式一(block形式)请求数据成功----------")
self.testManager.loadData()
}) { (failureResponse) in
print("--------------方式一请求数据失败----------")
}
- delegate使用:
1.创建一个继承XHNetworkDelegateManager的业务请求中间管理类:XHNetworkTestManager。这个类专门来实现一些公用的业务代理XHAPIManagerValidator、XHAPIManager等当然你也可以不用,那么你的代理实现就必须在具体的业务中,这样会增加业务代码量,所以不推荐。
2.调用
统一加载方法: loadData
加载更多方法:loadMoreData
//代理方式调用
self.testManager.loadData()
3.在具体业务中创建一个中间管理类的对象testManager
然后在业务接着完成剩余代理的实现
lazy var testManager:XHNetworkTestManager = {
let testManager = XHNetworkTestManager()
testManager.callBackDelagate = self
testManager.paramSourceDelegate = self
return testManager
}()
//参数代理,在这里传送请求参数
func configeApiParams(Manager manager: XHNetworkDelegateManager) -> [String : Any]? {
let dic = [String : Any]()
return dic
}
//返回数据代理
func requesApiSuccess(Manager manager: XHNetworkDelegateManager) {
print("--------------方式二(delegate形式)请求数据成功----------")
}
func requesApiFailure(Manager manager: XHNetworkDelegateManager) {
print("--------------方式二请求数据失败----------")
}
源码地址请点击这里,欢迎大神指导,若考虑不周的地方,请大家多提意见!如果喜欢或者对您有帮助,希望能给个小星星,多谢!