iOS 网络层设计
iOS 网络层 Engine 的功能
- 调用 Client 实现请求
- 配置请求信息
- 回调结果解析并返回到上层
- 取消请求
Engine 是使用层调用的,连接着 Client 和使用者。使用者通过 Engine 进行初步处理之后调用 Client。
iOS 网络层 Engine 的分类
Engine 会通过不同的分类,还会额外实现一些特殊的功能:每次都只会请求一次、不允许取消的、多次请求的等。以此,我将 Engine 暂时分类以下几类:
- 数据上传的 Engine;(UploadEngine)
- 获取服务器固定数据的 Engine;(DownLoadEngine)
- 需要多次请求的 Engine;(MutipleEngine)
数据上传的 Engine:由于取消请求并不能把你的请求从网络中取消掉,只能取消掉着陆点,所以上传的时候,不允许取消请求。而获取服务器固定数据的话,通过实例化的对象,以最后一次的调用为准,之前的请求全部取消。
获取服务器固定数据的 Engine:如果需要获取服务器的数据,一般都是以最后一次操作为准,如刷新或者加载更多,如果一次性多个请求,在 tableView 刷新数据的时候,有可能会发生刷新过程中,数据源被代替导致崩溃。
需要多次请求的 Engine:在例如多张图片上传的时候,需要判断哪个请求成功,哪个请求失败。在全部请求结束之后做什么操作。
iOS 网络层 Engine 的功能实现
首先是 BaseAPIConfig
的实现:
@interface BaseAPIConfig : NSObject
/// 地址
@property (nonatomic, copy) NSString *url;
/// 方法
@property (nonatomic, copy) NSString *method;
/// 参数
@property (nonatomic, copy) NSDictionary *param;
@end
BaseAPIConfig
是方法接口配置的参数,后面回根据需求或者分类不同而增加或减少。将参数提取出来单独归类是为了让方法的参数减少,并且在以后增加参数的时候,不需要增加方法,只需修改原先方法即可。
url
是请求地址,是所有的请求都需要的基础条件之一,不过有些情况会将url
写死。
method
是请求的方法;
param
是请求的参数,也是最核心的部分。可以上传的包括字典、数组、字符串等。
其余的参数可以根据需求进行更改,如果是图片或者文件或者其他的,也可以增加image等参数。
其次是 BaseEngine
的实现
/**
基础请求
*/
@interface BaseEngine : NSObject <NetClientDelegate>
/// 成功回调
@property (nonatomic, strong) void (^success)(NSDictionary* dict);
/// 失败回调
@property (nonatomic, strong) void (^fail)(NSDictionary* dict);
/// 数据任务
@property (nonatomic, strong) NSURLSessionDataTask *task;
/// 接口配置
@property (nonatomic, strong) BaseAPIConfig *config;
#pragma mark- method
/**
上传并返回数值
*/
- (void)startRequestWithConfig:(BaseAPIConfig *)config
successBlock:(void(^)(NSDictionary* dict))successBlock
failBlock:(void(^)(NSDictionary* dict))failBlock;
/**
上传并返回数值
*/
- (void)startRequestWithConfig:(BaseAPIConfig *)config
successBlock:(void(^)(NSDictionary* dict))successBlock;
/**
取消任务
*/
- (void)cancleTask;
@end
#import "BaseEngine.h"
@implementation BaseAPIConfig
@end
@implementation BaseEngine
#pragma mark- <NetClientDelegate>
- (void)netClientSuccess:(NSDictionary *)successDict WithTask:(NSURLSessionDataTask *)task {
self.task = nil;
if (self.success) {
self.success(successDict);
}
}
- (void)netClientFail:(NSDictionary *)failDict WithTask:(NSURLSessionDataTask *)task {
self.task = nil;
if (self.fail) {
self.fail(failDict);
}
}
#pragma mark- public method
- (void)cancleTask {
if (self.task && [self.task respondsToSelector:@selector(cancel)]) {
[[NetClient shareNetClient] cancleTask:self.task];
self.task = nil;
}
}
- (void)startRequestWithConfig:(BaseAPIConfig *)config
successBlock:(void(^)(NSDictionary* dict))successBlock {
[self startRequestWithConfig:config successBlock:successBlock failBlock:nil];
}
- (void)startRequestWithConfig:(BaseAPIConfig *)config
successBlock:(void(^)(NSDictionary* dict))successBlock
failBlock:(void(^)(NSDictionary* dict))failBlock {
self.success = successBlock;
self.fail = failBlock;
self.task = [[NetClient shareNetClient] postToMethod:config.method WithDict:config.param delegate:self];
}
@end
这是最基础的实现,实现的是普通的请求、也可以单独使用。
处理了以下几件事情:
- 将
config
进行解析,并传到client
中进行处理。 - 将接口的回调进行处理,并判断通过
block
返回到使用层。 - 允许使用层取消任务。
其他的几种分类都是为了实现不同方向的功能,在base
之上做的修改。
部分分类功能实现
// 数据获取
// DownLoadEngine
#pragma mark- public method
- (void)startRequestWithConfig:(BaseAPIConfig *)config
successBlock:(void(^)(NSDictionary* dict))successBlock
failBlock:(void(^)(NSDictionary* dict))failBlock {
// 只保存一次的task
if (self.task && [self.task respondsToSelector:@selector(cancel)]) {
[self.task cancel];
}
[super startRequestWithConfig:config successBlock:successBlock failBlock:failBlock];
}
// 数据上传
// UpLoadEngine
- (void)cancleTask {
return;
}
+ (void)startRequestWithConfig:(BaseAPIConfig *)config
successBlock:(void(^)(NSDictionary* dict))successBlock {
[[UploadEngine new] startRequestWithConfig:config successBlock:successBlock failBlock:nil];
}
+ (void)startRequestWithConfig:(BaseAPIConfig *)config
successBlock:(void(^)(NSDictionary* dict))successBlock
failBlock:(void(^)(NSDictionary* dict))failBlock {
[[UploadEngine new] startRequestWithConfig:config successBlock:successBlock failBlock:failBlock];
}
// 多次请求 Engine
// MultipleEngine
#import "MultipleEngine.h"
@implementation MultipleEngine
#pragma mark- <NetClientDelegate>
- (void)netClientSuccess:(NSDictionary *)successDict WithTask:(NSURLSessionDataTask *)task {
if ([self.tasksArray containsObject:task]) {
[self.tasksArray removeObject:task];
}
if (self.success) {
self.success(successDict);
}
}
- (void)netClientFail:(NSDictionary *)failDict WithTask:(NSURLSessionDataTask *)task {
if ([self.tasksArray containsObject:task]) {
[self.tasksArray removeObject:task];
}
if (self.fail) {
self.fail(failDict);
}
}
#pragma mark- public method
- (void)cancleAllTask {
for (NSURLSessionDataTask *task in self.tasksArray) {
[[NetClient shareNetClient] cancleTask:task];
}
[self.tasksArray removeAllObjects];
}
- (void)cancleTaskAtIndex:(NSInteger)index {
if (index >= self.tasksArray.count) {
return;
}
[[NetClient shareNetClient] cancleTask:self.tasksArray[index]];
[self.tasksArray removeObjectAtIndex:index];
}
- (void)cancleTask {
if (self.task && [self.task respondsToSelector:@selector(cancel)]) {
[[NetClient shareNetClient] cancleTask:self.task];
self.task = nil;
[self.tasksArray removeObject:self.task];
}
}
- (void)startRequestWithConfig:(BaseAPIConfig *)config
successBlock:(void(^)(NSDictionary* dict))successBlock
failBlock:(void(^)(NSDictionary* dict))failBlock {
// 只保存一次的task
[super startRequestWithConfig:config successBlock:successBlock failBlock:failBlock];
[self.tasksArray addObject:self.task];
}
#pragma mark - getter/setter
- (NSMutableArray *)tasksArray {
if (!_tasksArray) {
_tasksArray = [NSMutableArray array];
}
return _tasksArray;
}
@end
多次请求的修改的比较多,原因是他需要从单个任务转换为任务数组,所有跟任务有关的都需要重写导致的。
至此,大致的Engine
功能已经实现。
网友评论
1. 对应接口的实现方法中的接口名与内容(.m 文件)
2. 对应接口的声明方法中的接口名(.h 文件)
3. 调用对应接口的地方中的接口名与参数
如果是 BaseAPIConfig 的话,需要做的改变有:
1. BaseAPIConfig 中增加一个 cachePolicy 的参数。
2. 对应接口的实现方法中的内容(.m 文件)
3. 调用对应接口的地方中的 BaseAPIConfig 增加一个参数
如果愿意在 Client 层中拆解 BaseAPIConfig 的话,第二点的改变都可以忽略。
可以看出来的是,使用 BaseAPIConfig 之后,需要修改的地方,从1.n个 2.n个 3.xn个 变为1. 1个 2. xn 个
且许多的时候x = 1