本文分两个章来讲
第一章介绍 NSURLSession
第二章对AFNtworking 源码流程分析
AFNetworking
是一个对NSURLSession
高度封装的网络请求工具,所以先简单介绍一下NSURLSession及其用法;第二章再看AFNetworking 源码的时候不会懵;
1.1 NSURLSession
* NSURLSession支持http2.0协议
* iOS9.0之后使用的NSURLConnection(iOS9.0之后过期)
* 处理下载任务的时候可以直接把数据下载到磁盘中
* 支持后台下载和上传
* 同一个session发送多次请求,只需要建立一次连接(复用了TCP)
* 提供了全局的session并且可以统一配置,使用更加方便
* 下载的时候是多线程异步处理,效率更高
1.2 NSURLSessionTask
* NSURLSessionTask本身是一个抽象类,在使用的时候,通常是根据具体的需求使用它的几个子类
* NSURLSessionDataTask可以用来发送常见的Get,Post请求,既可以用来上传也可以用来下载
* NURLSessionDownloadTask可以用来发送下载请求,专门用来下载数据
* NSURLSessionUploadTask可以用来发送上传请求,专门用来上传数据
1.3案例 (利用NSURLSession发送get请求)
/* 这里抓了[喜马拉雅]的请求*/
-(void)test1{
/* 创建URL*/
NSURL *url = [NSURL URLWithString:@"http://mobile.ximalaya.com/dog-portal/checkOld/ios_remote/1583898209.875697?signature=34dc9a17ee0c44faac51f9db4ee22aac"];
/* 创建request*/
NSURLRequest *request = [NSURLRequest requestWithURL:url];
/*创建一个session 管理tastk请求任务*/
NSURLSession *sharedSession = [NSURLSession sharedSession];
/* 创建task任务*/
NSURLSessionDataTask *getTask = [sharedSession dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"get responseData:%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
/* 执行task*/
[getTask resume];
}
----------------------------------------------------------------------
/*运行结果*/
runtime[38079:5401322] delegate response didReceiveData:{
msg = 5;
ret = 5;
}
1.4案例(利用NSURLSession发送POST请求)
/* 这里抓了[喜马拉雅]的请求*/
-(void)test2{
NSURL *url_post = [NSURL URLWithString:
@"http://mobile.ximalaya.com/football-portal/diff2/batch?appId=1&groupNames=ios%2Cfufei%2Ctoc%2Caccount%2Clive%2Csys%2Ctob%2Cad%2Ccommunity%2Cmermaid%2Capm&ts=1583898210&signature=b87c2373ba38307c780fb19a3e52e537"];
NSMutableURLRequest *request_post = [NSMutableURLRequest
requestWithURL:url_post];
request_post.HTTPMethod = @"POST";
request_post.HTTPBody = [@"name=jack&password=123"
dataUsingEncoding:NSUTF8StringEncoding];
NSURLSession *shareSession = [NSURLSession sharedSession];
NSURLSessionDataTask *task_post = [shareSession dataTaskWithRequest:request_post completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"post responseData:%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
[task_post resume];
}
----------------------------------------------------------------------
/*运行结果*/
runtime[33254:4904562] post responseData:{
msg = 5;
ret = 5;
}
1.5案例(通过代理获取请求的结果)
-(void)test3{
NSURL *url = [NSURL URLWithString:@"http://mobile.ximalaya.com/dog-portal/checkOld/ios_remote/1583898209.875697?signature=34dc9a17ee0c44faac51f9db4ee22aac"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration
defaultSessionConfiguration];
/*创建session 并设置代理为 self (self 为当前ViewController)*/
NSURLSession *session = [NSURLSession
sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [session
dataTaskWithRequest:request];
[task resume];
}
/* 1.接收到服务器响应的时候调用该方法*/
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
self.mData = [NSMutableData data];
completionHandler(NSURLSessionResponseAllow);
}
/* 2.接收到服务器返回数据的时候会调用该方法,如果数据较大那么该方法可能会调用多次*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
/*拼接服务器返回的数据 */
[_mData appendData:data];
NSLog(@"delegate response didReceiveData:%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}
/* 3.当请求完成(成功|失败)的时候会调用该方法,如果请求失败,则error有值*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
/* 解析服务器返回的数据*/
/*因为此处返回的数据是JSON格式的,因此使用NSJSONSerialization进行反序列化处理*/
/* AFN中 只接受数据,交给responseSerializer 来处理*/
NSLog(@"delegate response didCompleteWithError:%@",[NSJSONSerialization JSONObjectWithData:_mData options:kNilOptions error:nil]);
}
----------------------------------------------------------------------
/* 运行结果*/
/*该打印是didReceiveData 代理方法的(因为数据比较小,所以一次可以解析)*/
runtime[33254:4904562] delegate response didReceiveData:{
msg = 5;
ret = 5;
}
/*该打印是 didCompleteWithError 代理方法打印的,获取的整个Data 数据*/
runtime[33254:4904562] delegate response didCompleteWithError:{
msg = 5;
ret = 5;
}
2. AFNetworking 开讲
在看文章的同时,可以看我画的这张时序图,希望读者能通过该图对AFNetworking 网络请求的逻辑流程有一个清晰的认识(这个图花了我好几个小时画);
众所周知,AFNetworking
实现了http 网络请求的 GET
、HEAD
、 POST
、PUT
、 PATCH
、DELETE
等方法,且提供了监听网络进度的block、失败的block、失败的block,其核心就是AFHTTPSessionManager
这个类;
AFHTTPSessionManager
主要实现了不同网络请求的调度和网安全策略的可配置、请求序列的可以配置、相映序列的可配置操作;
AFURLSessionManager
是 AFHTTPSessionManager
的父类,实现了更管理task请求任务的管理工作,及数据响应处理的操作。
AFNetworking
提供了securityPolicy
,requestSerializer
,responseSerializer
等参数方便开发者扩展以及自定义 ,以适配不同的服务端。
2.1 源码解析开始喽
当我们使用AFHTTPSessionManager
时候,第一件事就是初始化这个类,因为这是一个单例,我们来看一下 AFHTTPSessionManager 初始化的时候都做了哪些事情;
- (1) 调用父类的
- (instancetype)initWithSessionConfiguration:
方法; - (2) 配置URL(如果URL不为null)
- (3) 初始化requestSerializer 属性为 默认配置
[AFHTTPRequestSerializer serializer]
- (4)初始化AFJSONResponseSerializer 为
[AFJSONResponseSerializer serializer]
代码如下
- (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 调用了父类的初始化方法,我们看一下 AFURLSessionManager 初始化的时候做了哪些事情:
- (1)初始化会话配置(session configuration),默认为defaultSessionConfiguration
- (2)初始化队列,设置队列的最大并发数1
- (3)初始化会话session,并将session的代理对象设置为self,并将代理队列(
设置代理很重要
)。 - (4)初始化 responseSerializer 并设置为默认
[AFJSONResponseSerializer serializer]
- (5) 初始化securityPolicy 安全策略
- (6) 初始化网络状态监控(watch_OS)
- (7) 初始化mutableTaskDelegatesKeyedByTaskIdentifier 字典(很重要)
- (8)初始化线程锁
- (9)最后一大坨代码是为已经存在的task设置delegate(也很重要)
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
[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;
}
以POST
请求为例子,平时大家的写法基本是这样子的。
示例代码如下:
/* 向http://www.xxx.com/path/request 发送一个post 请求,参数为 @{} */
NSURLSessionDataTask *task = [[AFHTTPSessionManager manager ]POST:@"www.xxx.com/path/request" parameters:@{} progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
/* 取消操作*/
[task cancel];
/* 取消操作*/
[task suspend];
/* 再次执行*/
[task resume];
点进去查看 -(*) POST: parameters: success: failure:
函数的实现;
在这个函数中,做了如下几件事:
- (1) 调用自身的
- (*)POST: parameters: progress: success: failure:
方法 返回一个NSURLSessionDataTask
对象;
接着往下走看 - (*)POST: parameters: progress: success: failure:
函数的实现;
在这个函数中,做了如下几件事:
- (1) 调用父类AFURLSessionManager的
- (*)dataTaskWithHTTPMethod: URLString: parameters: uploadProgress:downloadProgress: success: failure:
获取SessionDataTask
对象; - (2) 执行resume 发送网络请求;
SessionDataTask 被外部对象获取,外部对象可对该 SessionDataTask 对象进行cancel
、suspend
、resume
等操作;
代码如下:
/* AFHTTPSessionManager.m 173*/
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
return [self POST:URLString parameters:parameters progress:nil success:success failure:failure];
}
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
/* 该方法调用子类的 dataTaskWithHTTPMethod 方法获取到请求的 NSURLSessionDataTask */
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
/*开始请求*/
[dataTask resume];
return dataTask;
}
进入父类AFURLSessionManager
查看 - (*)dataTaskWithHTTPMethod: URLString: parameters: uploadProgress:downloadProgress: success: failure:
函数的实现;
在这个函数中,做了如下几件事:
- (1) 通过requestSerializer 创建适合的Request请求;
- (2) 如果request 创建失败,响应failure block;
- (3) 通过dataTaskWithRequest 方法获取 dataTask 对象;
代码如下:
/* AFURLSessionManager.m (267行)*/
- (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;
/*self.requestSerializer 在 初始化的时候默认创建(也可外部配置)*/
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
/* 如果创建失败,进入该if 判断,执行failure block*/
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__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;
}
接着往下走看一下 - (*)dataTaskWithRequest: uploadProgress: downloadProgress: completionHandler:
函数的实现;
在这个函数中,做了如下几件事:
- (1) 线程安全的情况下,创建了一个
dataTask
对象(这么写是为了解决一个bug
issues 链接); - (2) 调用自身的
- (void)addDelegateForDataTask: uploadProgress: downloadProgress: completionHandler:
方法, 然后把dataTask、uploadProgressBlock、downloadProgressBlock、completionHandler几个参数传进去;
代码如下:
- (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];
});
/* 利用task对创建 AFURLSessionManagerTaskDelegate 对象和manager 的绑定关系*/
/* 说人话,就是manger 持有多个 AFURLSessionManagerTaskDelegate 对象*/
/* AFURLSessionManagerTaskDelegate 对象持有task 任务,响应URLSessionTask的代理方法,并响应uploadProgressBlock、 downloadProgressBlock、completionHandler的 block*/
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
接着往下看 - (void)addDelegateForDataTask: uploadProgress: downloadProgress: completionHandler:
函数的实现;
- (1) 创建AFURLSessionManagerTaskDelegate 对象;
- (2) delegate和manger 建立持有关系(weak);
- (3) 调用 - (void)setDelegate: forTask: 方法 ,以task.taskIdentifier 为key , delegate为value 存入manger 的mutableTaskDelegatesKeyedByTaskIdentifier 的可变字典中;
- (4)赋值completionHandler、uploadProgressBlock、downloadProgressBlock 几个block 给delegate 对象;
再次解释一下
delegate 表示 AFURLSessionManagerTaskDelegate 对象;
AFURLSessionManager 对象
通过delegate
来对task
任务进行管理,因为delegate
对象持有task
任务 并且实现了URLSessionTask
的代理方法,当网络请求发送出去并收到的时候会触发代理方法,获取网络请求的数据,不论成功还是失败都有响应;而且一开始delegate
创建的时候赋值了completionHandler
等block,所以代理方法处理完网络请求的数据就就可以调用completionHandler
的 block方法了,最后把数据通过completionHandler
block 传递出来;
代码如下:
/* AFURLSessionManager.m (584)*/
- (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 来衔接 SessionTask 和manage的关系*/
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
/* delegte 的manger 指向self,也就是多个delegate 通过weak 弱引用manager*/
delegate.manager = self;
/* task uploadProgressBlock 赋值给delegate 对象的*/
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
/* task uploadProgressBlock 赋值给delegate 对象的*/
delegate.uploadProgressBlock = uploadProgressBlock;
/* task downloadProgressBlock 赋值给delegate 对象的*/
delegate.downloadProgressBlock = downloadProgressBlock;
}
AFURLSessionManagerTaskDelegate
的实现截图如下,可以看出来SessionManagerTaskDelegate
对象实现了NSURLSessionTaskDelegate
、NSURLSessionDataDelegate
、NSURLSessionDownloadDelegate
的代理方法,因为AFNetworking
不止有简单的http请求,还有大文件的上传和下载的操作,
这里只对AFURLSessionManagerTaskDelegate
实现代理方法的逻辑做简单分析;
代码如下:
/*
AFURLSessionManagerTaskDelegate 实现了NSURLSessionTaskDelegate代理方法
*/
#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
/*省略部分代码...*/
__block id responseObject = nil;
if (error) {
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
/* 如果completionHandler 不为null task.response, responseObject 和 error 信息传递出去*/
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, error);
}
此处省略部分代码...
});
}else{
/*网络请求不出错的情况*/
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
此处省略部分代码...
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
/* 调用completionHandler block 回传 response,responseObject,error等信息*/
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, serializationError);
}
/*主线程发送通知-处理其它逻辑*/
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
}
最后- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
代理方法执行完做了什么事:
- (1) 调用completionHandler 函数处理网络请求的成功和失败(
很重要
)
注意看
代码是不是又回到了最开始,bolck 实现的地方,是不是AFHTTPSessionManager 这个block ,对的,就是这里;
至此 ,还没有完全结束;
因为AFURLSessionManager
在 - ()initWithSessionConfiguration:
的时候将 NSURLSession 的delegate
设置成自己,且实现了 代理方法 - (void)URLSession: task: didCompleteWithError:
,所以最后shareManager 最后需解除mutableTaskDelegatesKeyedByTaskIdentifier 可变字典中 key 和value 的绑定关系(key 为 task.taskIdentifier ,value 为 AFURLSessionManagerTaskDelegate 对象);
代码如下:
/* AFURLSessionManager.m (1061 行)*/
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
/* 通过task 获取 AFURLSessionManagerTaskDelegate 对象*/
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
/* 如果delegate 对象存在,调用 delegate removeDelegateForTask 方法*/
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
/* 移除 task 的绑定操作*/
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
/* AFURLSessionManager.m (636 行)*/
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
/* 在线程锁安全的情况下*/
[self.lock lock];
/*发送移除某个task 的通知*/
[self removeNotificationObserverForTask:task];
/*移除self mutableTaskDelegatesKeyedByTaskIdentifier 字典中key 为 task.taskIdentifier ,value 为delegate 对象的键值对*/
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}
至此 AFNetworking
源码的核心流程基本走完了;
额外扩展讲解
AFURLSessionManagerTaskDelegate
还实现的NSURLSessionTaskDelegate
和NSURLSessionDownloadDelegate
代理方法,用来处理上传和下载的网络请求操作;
#pragma mark - NSURLSessionDataDelegate
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
省略代码...
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
省略代码...
}
#pragma mark - NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
省略代码...
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
省略代码...
}
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location{
省略代码...
}