iOS – AFNetworking部分解析

代码结构分析

  • AFNetworking.h 中引入了框架所需的几个头文件
  • NSURLSession: 封装了不同类型的网络请求
  • Reachability: 封装网络状态管理器 (例如WIFI和WWAN)
  • Security: 封装HTTPS证书类
  • Serialization: 请求的序列化操作 (包装请求头、请求体,解析响应头、响应体)
  • UIKit: 各种分类解决实际问题

NSURLSession: 网络请求核心代码文件

AFCompatibilityMacros.h 兼容性宏

AFURLSessionManager

我认为这是对iOS原生网络请求的封装,其中包括使用各种URLSessionTask, 从底层实现数据请求,下载,上传任务。

AFURLSessionManager 同时实现了
NSURLSessionDelegate, (session失效,认证等)
NSURLSessionTaskDelegate, (重定向等,发送数据等)
NSURLSessionDataDelegate, (接收到响应等)
NSURLSessionDownloadDelegate (下载任务)

所以AFURLSessionManager对外开放了很多block的setter,间接去设置实现这些代理方法。

另外AFURLSessionManager还管理了网络请求的通知功能,下载完成,网络失败等,都通过AFURLSessionManager去发送通知。

AFURLSessionManager.h

// 核心session
@property (readonly, nonatomic, strong) NSURLSession *session;

// 代理回调的执行队列
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;

// 执行请求后返回的响应序列 (一定不为空)
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;

// 安全策略,默认使用defaultPolicy
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;

// 网络状态管理器
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;

// 正在运行的任务 (数据,上传,下载任务)
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;

// 下载完成时调用的block所在线程,如果为空则为主线程
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;

// 分组
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;


// 初始化 (指定初始化构造器)
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;

// 使session管理器无效,可选择是否取消挂起的任务
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;

// 创建一个数据请求任务
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler DEPRECATED_ATTRIBUTE; 
                            
// 创建一个上传任务 (本地文件,formData,或者流的方式)
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromFile:(NSURL *)fileURL
                                         progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
                                completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError  * _Nullable error))completionHandler;
                             
// 创建一个下载任务 (请求,或者resume data重新下载)
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                             progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
                                          destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                    completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;

// 返回指定任务的上传进度    
- (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;

// 返回指定任务的下载进度
- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;

- (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block;


/**
* 设置Task Delegate的回调
*/

// 设置session变无效时的block 
(void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block;

// 设置需要stream时的block
// 设置重定向时的block
// 设置身份验证时的block
// ...

// 设置跟踪上传进度的block
- (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block;

/**
* 设置Data Task Delegate回调
*/

// 设置接收到响应时的block
- (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block;

// 设置数据任务变为下载任务时的回调
- (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block;

// 设置接收数据时的回调
- (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block;

// 确定任务的缓存行为的回调
- (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block;

// 下载任务完成时的回调
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable  (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;

// 跟踪下载任务的进度
// 下载任务恢复时的回调

/**
* 通知
*/

AFNetworkingTaskDidResumeNotification           任务恢复
AFNetworkingTaskDidCompleteNotification         任务完成
AFNetworkingTaskDidSuspendNotification          任务暂停
AFURLSessionDidInvalidateNotification           Session无效
AFURLSessionDownloadTaskDidFailToMoveFileNotification   下载时无法移动到临时文件
AFNetworkingTaskDidCompleteResponseDataKey      完成响应
AFNetworkingTaskDidCompleteSerializedResponseKey    完成响应的序列化
AFNetworkingTaskDidCompleteResponseSerializerKey
AFNetworkingTaskDidCompleteAssetPathKey         相关的文件下载路径
AFNetworkingTaskDidCompleteErrorKey             任务出错

核心代码分析

AFURLSessionManager.m

// 初始化
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {

    // 各种初始化
    // ...
    
    // 防止后台回来后重新初始化这个session
    [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        for (NSURLSessionDataTask *task in dataTasks) {
            [self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
        }

        for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
            [self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
        }

        for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
            [self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
        }
    }];
    
    return self;
}

// 新建下载任务
- (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 {

    // 安全的创建dataTask
    __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;
}


// 新建上传任务 (以二进制为例,还有流,文件方式)
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromData:(NSData *)bodyData
                                         progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    __block NSURLSessionUploadTask *uploadTask = nil;
    url_session_manager_create_task_safely(^{
        uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
    });

    [self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];

    return uploadTask;
}

// 下载任务类似

在上述 创建数据任务,下载任务,上传任务时,AFN做了一个安全创建任务的方式,所以无论是什么样的任务,都是通过session去生成

dataTask = [self.session dataTaskWithRequest:request];
uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
downloadTask = [self.session downloadTaskWithRequest:request];

创建好任务后,调用了一系列addDelegate方法。

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

上述方法调用了 AFURLSessionManagerTaskDelegate 对象。该对象的具体结构:

// 弱引用
@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;

// 各种block
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;

继续回到上述创建Task的过程。
创建完Task后,需要通过addDelegateXXX,具体细节如下: (以数据请求任务为例)

- (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
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;
    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;

    // 可给任务设置一个可读的描述,在如果需要界面展示时使用
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

}

创建taskDelegate后,对其属性进行赋值。然后调用了setDelegate方法,细节如下:

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}

上述代码中,通过 task.taskIdentifier 作为唯一标识符保存TaskDelegate。

addNotificationObserverForTask 则是将task与Resume和Suspend通知关联起来,当向通知中心发送通知时,可以一并带上发生该事件的任务。

所以在AFURLSessionManager中,并没有真正的去进行网络请求,只是实现了对task的封装。

AFHTTPSessionManager 真正实现网络请求

本类继承自上述AFURLSessionManager类,我们在使用AFNetworking的时候也是基本使用该类。

该类有如下属性及方法


// 请求的url
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;

// 序列化
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;

@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;

// 单例
+ (instancetype)manager;


- (instancetype)initWithBaseURL:(nullable NSURL *)url;
- (instancetype)initWithBaseURL:(nullable NSURL *)url
           sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
           
// GET
// HEAD
// POST
// PUT
// PATCH
// DELETE

这里以GET请求为例

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

这一步很简单,无非就是调用一个方法获取到任务,然后执行任务即可。常用的POST也是基本一样的代码。

核心方法在于 dataTaskWithHTTPMethod

- (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
{
    NSError *serializationError = nil;
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {
        // 如果序列化出错,则调用failure()
    }

    // 通过下面的方法生成一个dataTask
    __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;
}

所以头文件提供的GET,POST等方法,只是为了外借使用方便,其本质还是调用上述方法,通过传入字符串"GET", "POST" 等,来生成一个URLRequest, 然后在通过这个request生成 URLSessionDataTask。 返回的task经过GET,POST等方法启动。

第一步是通过传入的method,url等字段序列化生成URLRequest

此过程在AFURLRequestSerialization类中

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(method);
    NSParameterAssert(URLString);

    NSURL *url = [NSURL URLWithString:URLString];

    NSParameterAssert(url);

    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    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;
}

主要设置部分头部信息。

第二步通过request,生成dataTask

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

所以这里又回到了AFURLSessionManager。

AFURLSessionManagerTaskDelegate 对Task进行封装

前面我们提到了,NSURLSession系列中有三种常用的协议,可以去监听请求是否发生变化,下载等过程。

本来Task对象要实现协议,然后判断任务的状态。
通过该类,可以为每一个Task对象设置回调,相当于每个Task和回调事件进行绑定。

这里延伸我自己对该做法的思考

之前有需求需要实现一个页面中出现多个tableView/collectionView,这样在ViewController中实现协议时,需要不断的去判断到底是哪个tableView在调用协议方法,然后去做特定的实现。

通过AFN的做法,本来是多个object对应一组协议实现,现在相当于object和协议实现一一对应。

AFNetworkReachabilityManager 网络状态管理器

该类可以查看当前的网络状态,WIFI,流量,还是没有网络等

主要是对SCNetworkReachabilityRef 的封装

分析

我对NSURLSession一类的理解

我觉得可以以请求 https://xxx.com/example.json 为例,解释一下整个请求过程。

  1. 将urlString转为NSURL;
  2. 通过NSURL创建NSRequest (NSMutableRequest), Request可以设置请求头等信息;
  3. 通过NSURLSessionConfiguration实例化一个NSURLSession对象,我认为NSURLSession才是网络请求的核心所在,可以通过Session对象生成任务;
  4. 通过给Session对象传入Request创建NSURLSessionDataTask, 在生成task的同时,可以为任务设置请求结束后的回调。
  5. 调用 [task resume] 启动任务。

NSURLSession 与cookies,证书等相关。

我对AFNetworking的理解

目前常用的两个类是AFURLSessionManager和AFHTTPSessionManager,后者继承于前者。

AFURLSessionManager 主要是生成各种NSURLSessionTask,例如在做下载功能之前,可能需要通过AFURLSessionManager生成一个downloadTask,然后单独对该task进行操作。

AFHTTPSessionManager 则是对常用的网络请求进行封装,例如GET,POST等操作,并且通过对需要写入请求的数据进行序列化,最后运行请求任务。

因为NSURLSession系列可以遵守多个协议,从AFNetworking的编码来看,框架对外提供了大部分setter来设置回调Block。也就是说,AFN实现的NSURLSessionXXXDelegate, 仍然是在AFN框架内部实现协议里面的方法,不过方法内部的具体实现,都是调用的Block,而这些Block都是由使用AFN的项目通过setter去设置的。