[iOS]简读AFNetworking

一.背景

之前没怎么太过关注AFN的源码,最近花了一点时间读了一下,感觉获益匪浅,也学到了不少的小技巧.这里不探讨具体的使用,重点在于请求过程以及一些注意点.

二.AFN的网络请求过程

1.这里先要介绍几个类

AFHTTPSessionManager:这个类就是我们经常使用的类了,我们就是通过这个类完成了请求任务.
AFURLSessionManager:AFHTTPSessionManager继承自AFURLSessionManager,它内部封装了NSURLSession的delegate方法,是最基本的请求类.
AFURLSessionManagerTaskDelegate:这是内置于AFURLSessionManager的一个类,它实现了NSURLSession请求的数据转发和回调.

2.过程分析

AFHTTPSessionManager

分析之前,我们先看看AFHTTPSessionManager的初始化过程,聚焦到其中这个最底层的初始化方法:

- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }

    // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
        url = [url URLByAppendingPathComponent:@""];
    }

    self.baseURL = url;
    //这里有默认解析方式
    self.requestSerializer = [AFHTTPRequestSerializer serializer];
    self.responseSerializer = [AFJSONResponseSerializer serializer];

    return self;
}

所以如果我们不设置AFHTTPSessionManager的解析方式,那么默认是AFHTTPRequestSerializer,AFJSONResponseSerializer.我看到某些项目中还在外部设置了解析方式,如果和默认的一样,其实可以省略.

以GET请求为例

当我们command + 左键进入GET请求方法内时,可以看到有如下两个API:

- (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 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
{

    NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                        URLString:URLString
                                                       parameters:parameters
                                                   uploadProgress:nil
                                                 downloadProgress:downloadProgress
                                                          success:success
                                                          failure:failure];

    [dataTask resume];

    return dataTask;
}

我们可以发现

- (NSURLSessionDataTask *)GET:(NSString *)URLString
                   parameters:(id)parameters
                      success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                      failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure

这个API底层调用的是:

- (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

他们之间的区别后者在于多了一个progress回调.这个progress回调是用来回调下载进度的.

然后就到了不管是GET,POST等都会调用的底层方法:

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure{
    //(1)判断解析是否成功                                    
    NSError *serializationError = nil;
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    //如果解析错误直接返回
    if (serializationError) {
        if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
            //(2)如果请求中用异步线程请求,完成就用主线程进行回调.
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
#pragma clang diagnostic pop
        }

        return nil;
    }
    //(3).调用父类AFURLSessionManager的方法进行数据回调
    __block NSURLSessionDataTask *dataTask = nil;
    dataTask = [self dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];

    return dataTask;
}

在这个方法中我们可以看到如下几点:

(1).先判断解析是否成功,成功之后才会继续请求.

(2).然后利用

dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });

这种形式进行数据回调,如果我们有自定义的队列,这里就是completionQueue,那么就用它进行回调,没有就放在主线程回调.

(3).这个方法调用了父类AFURLSessionManager

- (NSURLSessionDataTask *)dataTaskWithRequest:request
                          uploadProgress:uploadProgress
                        downloadProgress:downloadProgress
                       completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error)

那么接下来就到AFURLSessionManager了.

AFURLSessionManager

这里定位到如下代码:

- (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;
    //(1)安全的创建一个dataTask
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });
    //(2)将task进行转发,得到请求完成的回调
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}

这里我们有两点需要注意的:

(1).url_session_manager_create_task_safely是为了防止在iOS8之前的一个bug,当重复创建task时,新建的task可能会把之前的task覆盖,造成数据回调异常.所以这里抛了一个队列url_session_manager_creation_queue用来解决这个问题.

#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0
......

static void url_session_manager_create_task_safely(dispatch_block_t block) {
    if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
        // Fix of bug
        // Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
        // Issue 这里是bug地址 about:https://github.com/AFNetworking/AFNetworking/issues/2093
        dispatch_sync(url_session_manager_creation_queue(), block);
    } else {
        block();
    }
}

不过这个问题在iOS8已经以及修复了.

(2)这里用了一个addDelegateForDataTask方法来接收这个task.目的是转发这个task到AFURLSessionManagerTaskDelegate中,让这个类完成最后的数据转发,并回调.

到这里需要中断一下,因为有个疑问,为什么AFN要设计这样一个类来完成请求任务?我们先看看AFURLSessionManagerTaskDelegate这个类.

AFURLSessionManagerTaskDelegate

先看它的声明:

@interface AFURLSessionManagerTaskDelegate : NSObject 
@property (nonatomic, weak) AFURLSessionManager *manager;
@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSProgress *uploadProgress;
@property (nonatomic, strong) NSProgress *downloadProgress;
@property (nonatomic, copy) NSURL *downloadFileURL;
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
@end

从中我们可以看出如下几点:
(1).它遵守了NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate这三个协议,并且实现了这三个协议方法.

(2).它持有了AFURLSessionManager,并且用weak进行修饰.并且定义了如下属性:

mutableData:获取的数据data.
uploadProgress:上传的进度.
downloadProgress:下载的进度.
downloadFileURL:下载的文件的URL.
downloadTaskDidFinishDownloading:完成下载的block.
uploadProgressBlock:回调上传进度的block.
downloadProgressBlock:回调下载进度的block.
completionHandler:任务完成的回调,从这个block的定义可以看出,就是这个block回调了我们需要的responseObject,error.

typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);

这里就大概能猜测出AFURLSessionManagerTaskDelegate的作用就是管理进度和完成数据的转发和回调.

这里关于上传和下载进度管理进行略过...主要看一下网络数据是怎么来和回调的.

我们可以发现下面实现了三个NSURLSession的代理方法:

//完成数据转发和回调给上层
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error;
//获取下载的数据
- (void)URLSession:(__unused NSURLSession *)session
          dataTask:(__unused NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data;
//完成下载任务时,获取下载的文件地址,并且将文件写入到改地址中.  
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location;

这里先暂告一段落,我们跳回到AFURLSessionManager,实际上完成请求任务的还是AFURLSessionManager.

看它的初始化方法中的这一句代码:

self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

说明底层是通过NSURLSession的delegate这种方式来进行请求.
我们忽略其他处理,又回到addDelegateForDataTask这个方法:

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    //生成一个delegate
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
    //持有当前的这个AFURLSessionManager,这里决定了为啥manager要用weak修饰.
    delegate.manager = self;
    //完成请求回调的block
    delegate.completionHandler = completionHandler;

    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    //将这个task进行转发,让这个delegate去处理task.
    [self setDelegate:delegate forTask:dataTask];
    
    //上传和下载的进度
    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

setDelegate中:

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);
    //加锁
    [self.lock lock];
    //保存这个delegate
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [delegate setupProgressForTask:task];
    [self addNotificationObserverForTask:task];
    //解锁
    [self.lock unlock];
}

同样还有一个delegateForTask方法:

- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
    NSParameterAssert(task);

    AFURLSessionManagerTaskDelegate *delegate = nil;
    //加锁
    [self.lock lock];
    //得到保存的delegate
    delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
    //解锁
    [self.lock unlock];

    return delegate;
}

这里有加锁的目的是为了防止多个请求存在时,造成delegate存取冲突.

然后直接跳入相关delegate方法中去:

- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    //(1)获取当前任务的处理者
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    // delegate may be nil when completing a task in the background
    //(2)这里delegate可能为空
    if (delegate) {
        //(3)转发task
        [delegate URLSession:session task:task didCompleteWithError:error];
        //(4)完成请求移除task
        [self removeDelegateForTask:task];
    }
    //(5)任务完成
    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }
}

前面说过AFURLSessionManagerTaskDelegate已经实现了NSURLSessionDataTaskDelegate的三个方法,所以这里(3)实现了转发.这里再回到NSURLSessionDataTaskDelegate的这个代理方法中:

- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
//这里的task就是从前面转发过来的task,里面包含了下载的结果
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
    __strong AFURLSessionManager *manager = self.manager;

    __block id responseObject = nil;
    //这个字典保存了下载的一些信息,用于通知转发.
    __block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;

    //Performance Improvement from #2672
    //接收下载的数据
    NSData *data = nil;
    if (self.mutableData) {
        data = [self.mutableData copy];
        //We no longer need the reference, so nil it out to gain back some memory.
        self.mutableData = nil;
    }
    //保存下载完成的URL和data
    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }
    //如果error存在
    if (error) {
        userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;

        dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
            if (self.completionHandler) {
                self.completionHandler(task.response, responseObject, error);
            }

            dispatch_async(dispatch_get_main_queue(), ^{
                [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
            });
        });
    } else {
        //异步处理数据
        dispatch_async(url_session_manager_processing_queue(), ^{
            NSError *serializationError = nil;
            //解析数据
            responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];

            if (self.downloadFileURL) {
                responseObject = self.downloadFileURL;
            }

            if (responseObject) {
                userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
            }

            if (serializationError) {
                userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
            }

            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                if (self.completionHandler) {
                   //在主队列中回调请求的结果 
self.completionHandler(task.response, responseObject, serializationError);
                }

                dispatch_async(dispatch_get_main_queue(), ^{
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                });
            });
        });
    }
#pragma clang diagnostic pop
}

这里就是我们请求的数据最开始的出口了.所以到这里,我们的一个普通的请求过程就完成了.总结一下大概过程:

app->AFHTTPSessionManager->AFURLSessionManager->AFURLSessionManagerTaskDelegate
->AFURLSessionManager->AFHTTPSessionManager->app

到此为止,AFN请求过程就有一个基本的概念了,至于其他模块,暂时不去讨论.

你可能感兴趣的:([iOS]简读AFNetworking)