移动开发干货店简友广场

真的了解AFNetworking了嘛?!

2021-09-17  本文已影响0人  星辰_入海

### 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).

上一篇下一篇

猜你喜欢

热点阅读