YTKNetwork 源码解读 (一) 之 YTKBaseRequest

因为 YTKBaseRequest .h/.m 里代码数目不多,所以这里将从头开始逐行介绍,有一些直接就写在注释里了,有一些需要注意的地方会特别摘出来讲解的。

不知道你有没有注意到 NS_ENUM 后面没有跟着指定一个名字,是的,如果你不需要指定一个类型名字的话,可以直接这样子写。
YTKRequestValidationErrorInvalidStatusCode 值的是 reponse.statusCode 不在 200~299 这个区间内,跟 AFHTTPResponseSerializer 的 acceptableStatusCodes 的范围一致

常见的请求方法

请求的序列化样式,相对于 AFNetworking 少了 AFPropertyListRequestSerializer,可能是因为这种编码方式比较少见吧。

相应的序列化样式,相对于 AFNetworking 少了 AFXMLDocumentResponseSerializer,AFPropertyListResponseSerializer,AFImageResponseSerializer,AFCompoundResponseSerializer 几个类型

对应于 NSURLSessionTask 的 priority 属性,需要在 iOS8 以后的系统中使用,不过一般也不需要再兼容之前的系统了吧。。。

AFMultipartFormData 在 AFNetworking 用于 Content-Type application/form-data 的请求,将数据添加到请求体中。一般用于 upload task 中。
在 AFConstructingBlock 类型的 block 中,你可以将数据添加数据添加到请求体中
在 AFURLSessionTaskProgressBlock 的 block 你可以追踪上传的进度

你可以实现这两个协议,以便在 request 的不同阶段进行相应的处理
需要注意的是这些方法的执行顺序不要搞错了

接下来介绍 YTKBaseRequest 这个类,它是一个抽象类,提供了构建 requeset 时的许多选项

这里大部分的属性是映射 NSHTTPURLResponseNSURLRequestNSURLSessionTask 这几个类的属性的。

需要注意的是 responseObject,如果 resumableDownloadPath 不为空并且 requestTask 是 DownloadTask 类型的,那么这个属性的值就一个文件路径(NSURL),用来保存下载数据的

tag 可以用来标记 YTKBaseRequest,默认值是 0
userInfo 可以用来添加额外信息

requestAccessories 是一个数组,可以用来保存多个实现了 YTKRequestAccessory 协议的对象
constructingBodyBlock 用来将数据添加到请求体中
resumableDownloadPath 是保存下载数据文件的路径,当下载请求失败时,部分的下载数据会自动保存到这个文件中,否则数据会保存到 responseData/responseString 中
resumableDownloadProgressBlock 可以用来追踪下载进度
uploadProgressBlock 可以用来追踪上传进度

-setCompletionBlockWithSuccess:failure: 用来添加请求 成功/失败 的回调
-clearCompletionBlock: 将请求 成功/失败 的回调 block 置为 nil,避免循环引用
-addAccessory: 用来添加实现 YTKRequestAccessory 的对象

  1. -start:开启任务。需要注意是为了让 task 在完成之前不被释放掉,会在这个 task 添加到单例 YTKNetworkAgent 的成员变量 _requestsRecord
  2. -stop:取消任务。我感觉这个方法叫做 cancle 比较合适,我一开始看到 stop 还以为是 suspend 的意思
  3. -startWithCompletionBlockWithSuccess:failure:,设置请求 失败/成功 的回调,并开启任务

这部分是你实现 YTKBaseRequest 子类时可以覆写的方法

-requestCompletePreprocessor,-requestCompleteFilter,-requestFailedPreprocessor,-requestFailedFilter:这几个方法都是对请求 成功/失败 结果的处理,结合之前讲过 YTKRequestDelegateYTKRequestAccessory 两个协议里面的方法,下面给出这些方法的执行顺序.

请求成功后回调的执行顺序:

  1. requestCompletePreprocessor:如果使用 cache 的话是在主线程,否则的话在其它线程执行。下面的方法均在主线程执行
  2. requestWillStop
  3. requestCompleteFilter
  4. requestFinished
  5. successCompletionBlock
  6. requestDidStop

请求失败后回调的执行顺序

  1. requestFailedPreprocessor:如果使用 cache 的话是在主线程,否则的话在其它线程执行。下面的方法均在主线程执行
  2. requestWillStop
  3. requestFailedFilter
  4. requestFailed
  5. failureCompletionBlock
  6. requestDidStop

方法 -cacheFileNameFilterForRequestArgument 用来对请求参数 argument 进行过滤后返回一个新的参数,用在获取缓存文件名字上。

方法 -requestAuthorizationHeaderFieldArray,用于身份验证,在这个方法中你需要返回一个容量为 2 的数组,第一个元素表示账号,第二个元素表示密码。
该认证方式使用用户的 账号/密码 作为凭证信息,进行 base64 编码添加到请求头 Authorization 中传输到服务器中

方法 buildCustomUrlRequest,在这个方法里面你可以放回一个自定义的 request,而不是使用 AFNetworking 的 AFHTTPRequestSerializer 生成。
如果你返回了一个不为 nil 的对象,那么将忽略 requestUrl, requestTimeoutInterval, requestArgument, allowsCellularAccess, requestMethod and requestSerializerType

方法 jsonValidator,在这个方法里面,你可以对 reponse 序列化后的 JSON 对象进行验证。
举个例子,我们要向网址 http://www.yuantiku.com/iphone/users 发送一个 GET 请求,请求参数是 userId 。我们想获得某一个用户的信息,包括他的昵称和等级,我们需要服务器必须返回昵称(字符串类型)和等级信息(数值类型),则可以覆盖 jsonValidator 方法,实现简单的验证。

1
2
3
4
5
6
- (id)jsonValidator {
return @{
@"nick": [NSString class],
@"level": [NSNumber class]
};
}

方法 statusCodeValidator,我觉得如果是验证状态码的话好得加个状态码的参数啊,不过无所谓啦,反正可以自己获取状态码然后再进行判断。该方法返回一个布尔值,如果返回的是 NO 的话将会报错。

接下来讲 .m 文件

在 .h 文件中这些属性都是只读的,在 .m 文件中改成可读写,防止外部修改这些属性的值。

NSURLSessionTaskNSURLResponse 的一些属性映射成自己的属性,便于使用

设置请求 成功/失败 的回调
需要注意的是可以添加多个实现了 YTKRequestAccessory 协议的对象

开启/关闭 请求的方法。
在 start 方法中:

1
2
3
4
5
6
7
8
// 触发 YTKRequestAccessory 代理
- (void)toggleAccessoriesWillStartCallBack {
for (id<YTKRequestAccessory> accessory in self.requestAccessories) {
if ([accessory respondsToSelector:@selector(requestWillStart:)]) {
[accessory requestWillStart:self];
}
}
}

addRequest方法实现

在成功创建 task 后,task 将会被添加到 _requestsRecord 属性中避免被释放。随后调用 resume 开启任务
如果我们没有覆写方法 buildCustomUrlRequest 返回自定义的 request,系统会根据 YTKBaseRequest 创建 request。这部分在 sessionTaskForRequest:error: 中实现

获取 YTKBaseRequest 实例上各种属性的值,例如方法类型,参数,然后在根据 method 的不同,使用不同的方法创建 task。
因为这部分内容都在 YTKNetworkAgent,这里就简单提下。在后面讲解 YTKNetworkAgent 部分的时候再仔细说

stop 方法:

1
2
3
4
5
6
7
8
// 执行代理
[self toggleAccessoriesWillStopCallBack];
// 将 delegate 置为 nil
self.delegate = nil;
// task cancle
[[YTKNetworkAgent sharedAgent] cancelRequest:self];
// 执行代理
[self toggleAccessoriesDidStopCallBack];

如果你的请求是下载任务,并且你指定了一个缓存文件名,那么下载好的部分数据将会写入这个临时文件中,在下次恢复下载时使用。
当然如果要使用断点下载,还需要满足下面的几个条件:

  1. 这个资源自你第一次请求后没有改变
  2. 这个任务是一个 HTTP 或者 HTTP GET 请求
  3. 服务器在 reponse header 提供了 ETag 或者 Last-Modified 字段
  4. 服务器支持字节范围请求
  5. 本地临时文件没有被删除

由于 YTKBaseRequest 是一个基类,所以在这些需要子类覆写的方法里面内容不多

覆写了 -description 方法,方便打印信息

作者

千行

发布于

2020-07-13

更新于

2022-10-21

许可协议

评论