真的了解AFNetworking了嘛?!
### AFNetworking
AFNetworking是一款优秀的网络请求框架,从这篇文章开始,我们一起来看关于AFNetworking常用的源码解析。通常,在网络请求中,我们会进行GET和POST操作,面对这种方式,系统提供了NSURL和NSURLRequest来实现GET和POST请求。
系统方式:
```objectivec
NSURL *url = [[NSURL alloc] initWithString:@"https://www.baidu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:nil error:nil];
NSLog(@"%@",dict);
}];
[task resume];
```
AFNetworking方式:
```objectivec
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:(nonnull NSString *)
parameters:(nullable id)
headers:(nullable NSDictionary<NSString *,NSString *> *)
progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
```
> 思考:为什么开发中常用AFNetworking,而减少了系统原装的请求方式???
通过对比两种方式,我们不难看出AFNetworking给开发者直面的优点是集中性较强,而系统方式显得更加分散,这不便于后期修改和维护操作。具体内部是有和差异,我们拭目以待。
事实上,AFNetworking将系统的方法集中在封装就形成了现在这个优秀的框架。
#### GET方式
AFNetworking 提供了三种GET方式,但是有两种已经作废,我们来看一下,如果调用作废的它会怎么做。
```objectivec
- (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(id)parameters success:(void (^)(NSURLSessionDataTask *task, id responseObject))success failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure{
return [self GET:URLString parameters:parameters headers:nil progress:nil success:success failure:failure];
}
- (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(id)parameters progress:(void (^)(NSProgress * _Nonnull))downloadProgress success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure{
return [self GET:URLString parameters:parameters headers:nil progress:downloadProgress success:success failure:failure];
}
- (NSURLSessionDataTask *)GET:(NSString *)URLString parameters:(id)parameters headers:(nullable NSDictionary <NSString *, NSString *> *)headers progress:(void (^)(NSProgress * _Nonnull))downloadProgress success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure{...}
```
前面两个方法已经失效,因此,即使开发者调用了该方法,也会内部直接进行调用第三个方法,第三个看起来更加完善,包含了:
1. 传入参数
2. 下载进度
3. 成功回调
4. 错误回调
既然是封装,我们看系统之前,想要实现GET方法,是启动了一个SessionTask任务,并且通过resume进行开启任务。那么,我们猜测GET方法中提供的方式,肯定是返回了SessionTask,并且,已经为用户自动开启了任务,来使得编码更加简单,我们来验证一下这样的设计。
```objectivec
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
headers:headers
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}
```
的确是这样,又一次对NSURLSessionDataTask进行了封装。我们进入源码来分析一下,是如何封装的。
```objectivec
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
headers:(NSDictionary <NSString *, NSString *> *)headers
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
/* for-in循环遍历headers数组的key值 */
for (NSString *headerField in headers.keyEnumerator) {
/**
* request添加请求头
* headers[headerField] -- 取出value值
*/
[request addValue:headers[headerField] forHTTPHeaderField:headerField];
}
/* 如果进入了循环serializationError,则说明出现了错误 */
if (serializationError) {
if (failure) {
/* 如果出现了错误,则开辟一个线程,用来处理错误问题 */
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
/* __block参数表明是在block中使用的变量 */
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
/* dataTask在外部声明了为block类型 */
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
/* 返回最终的数据任务 */
return dataTask;
}
```
这一块代码我们分板块来分析。
首先,要生成NSURLSessionDataTask,需要一个Request,这里面AFNetworking采用了NSMutableURLRequest来生成NSURLSessionDataTask。
```objectivec
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
```
这里有一个self.requestSerializer,请求序列化,这个东西我们需要进一步的研究。
> /**
Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies.
@warning `requestSerializer` must not be `nil`.
*/
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
这个requestSerializer是通过协议来创建的。
> 默认情况下,此属性设置为“ AFJSONResponseSerializer”的实例。
看到这里,你肯定知道这或许又是一次封装,我们先跳过,看后面又做了什么操作。
第二步,通过forin循环遍历开发者传入的请求头,取出对应的value和key,保存在request中。
```objectivec
for (NSString *headerField in headers.keyEnumerator) {
[request addValue:headers[headerField] forHTTPHeaderField:headerField];
}
```
第三步,利用request生成一个dataTask并返回,这里对于dataTask又做了一次封装。
```objectivec
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
/* dataTask在外部声明了为block类型 */
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
```
第四步,分析封装的dataTask,此时传入的request是包含着key和value的。
```objectivec
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
```
```objectivec
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
```
这一部分,根据request创建了一个dataTask。
> 注意:如果要修改Block以外变量,应该将该变量设置为__block类型,从而可以在Block中进行赋值修改操作
>
>
> url_session_manager_create_task_safely传入的是一个Block
```objectivec
dispatch_sync(url_session_manager_creation_queue(), block);
```
其核心做的是,同步线程阻塞来创建dataTask。
> 思考:为什么要用同步阻塞线程,难道异步操作不可以吗???
AFNetworking之所以设置同步操作,是因为网络访问不是一次性的,一次GET请求是多次响应的过程,我们如果不用阻塞线程,就会使得创建的任务出现严重的错乱问题,所以,这里不能使用多线程的异步操作,而应该使用同步操作,来保证创建的dataTask是正确的。
剩下的操作,AFNetworking就交给了协议来做,我们先到此为止。
刚才还遗留一个问题,requestSerializer具体如何实现的。
#### requestSerializer
```objectivec
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
/* 如果url位false则插入断言 */
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
/* 设置HTTP访问的方法 */
mutableRequest.HTTPMethod = method;
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
```
其中内部通过url创建一个mutableURLRequest。
```objectivec
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
```
HTTPMethod设置了访问方式,这样就可以重复利用在GET和POST更多的访问方式上。
```objectivec
mutableRequest.HTTPMethod = method;
```
剩下部分就是进行对Request进行了设置。
至此,基本上对GET请求了有了一定的认识,对于更为具体详细的是如何设置Request,AFNetworking如何进行代理操作的,将在下一篇POST请求进一步分析。
#### 源码下载
AFNetworking中文源码下载: [GitHub](https://github.com/CreaterOS/ReadAFN.git).