此文章主要记录笔者在AFNetworking
源码阅读中的一下个人理解, 每次阅读都会记录一下, 如有错误, 请指正.
1. AFNetworking的使用
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer.timeoutInterval = 60.0f;
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html",nil];
[manager GET:@"" parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {
NSLog(@"%@", downloadProgress);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@", responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@", error);
}];
我们在使用AFNetworking
发送GET
请求时如上诉代码, 这里我们以一个GET
请求为例, 逐步分析代码.
AFNetworking
中核心的发送网络请求的代码在AFURLSessionManager
中, AFHTTPSessionManager
是AFURLSessionManager
的子类, 主要对HTTP
请求做了一写封装.
2. AFURLSessionManager实例创建
2. 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
{
// 创建一个task
NSURLSessionDataTask *extractedExpr = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
NSURLSessionDataTask *dataTask = extractedExpr;
// 执行task, 开始网络请求
[dataTask resume];
return dataTask;
}
这个方法看起来很简单, 根据传入的参数使用dataTaskWithHTTPMethod
方法创建dataTask
任务, 并且调用[dataTask resume]
方法开启任务, 将创建的dataTask
任务返回.
dataTask
任务可以进行cancel
, suspend
和resume
操作.
-
cancel
:取消当前的任务, 但是会标记一个被取消的任务, 任务取消以后会调用-URLSession:task:didCompleteWithError:
方法, 将错误信息{ NSURLErrorDomain, NSURLErrorCancelled }
传递出去. 处于suspended
状态的任务也可以被取消. -
suspend
:暂停当前任务, 被暂停的任务可能还会继续调用代理方法, 比如汇报接收数据的情况, 但是不会在发送数据. 超时计时器会在任务被暂停时挂起. -
resume
:不仅可以启动任务, 还可以唤醒状态为suspend
的任务.
3. dataTaskWithHTTPMethod方法
我们来看一下dataTaskWithHTTPMethod
方法是怎么创建dataTask
任务的.
- (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.将传入的参数, 以及其他参数创建一个request, 其中设置了header请求头, 将参数进行百分号编码
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) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
// 2.将request和session做关联, 在init的时候创建了session的代理, 利用session创建datatask, 以datatask的id作为key AFN代理作为value存入mutableTaskDelegatesKeyedByTaskIdentifier字典中, 建立关系. 利用session的代理将信息转发到AFN的代理中做统一处理.
__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;
}
可以看到这个方法内部分为了两部分, 第一部分是创建request
请求, 第二部分是通过request
请求创建dataTask
任务.
request
请求是通过AFURLRequestSerialization
的requestWithMethod
方法构建的,
关于 AFURLRequestSerialization
我会有一篇专门的文章去介绍,
附上传送门 AFNetworking之AFURLRequestSerialization深入学习.
这里我们主要讲解AFURLSessionManager
是如何处理dataTask
任务, 将代理方法在AFURLSessionManagerTaskDelegate
中做统一处理的.
4. 构建NSURLSessionDataTask
有了request
后, 就可以调用AFURLSessionManager
的方法来构建NSURLSessionDataTask
4.1 dataTaskWithRequest:方法
- (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 {
NSLog(@"%@", [NSThread currentThread]);
// 创建NSURLSessionDataTask
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
// 这里的作用:
// manager管理dataTask和afn自定义的delegate
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
同样这个方法也分为两个部分, 第一部分创建dataTask
任务, 第二部分调用addDelegateForDataTask
方法管理dataTask
和AFN
自定义的delegate
.
将dataTask
传入block
中用__block
修饰, 是指针传递, 这样就可以为dataTask
赋值.
4.2 url_session_manager_create_task_safely函数
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 about:https://github.com/AFNetworking/AFNetworking/issues/2093
// 串行队列中同步执行任务
dispatch_sync(url_session_manager_creation_queue(), block);
} else {
block();
}
}
使用url_session_manager_create_task_safely
函数创建dataTask
, 主要为了解决在iOS8以前的一个Bug
.
4.3 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
{
// 将AFURLSessionManagerTaskDelegate和manager建立关系
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
// delegate弱引用self
delegate.manager = self;
// 将回调赋值给delegate, 在delegate的代理方法中执行
delegate.completionHandler = completionHandler;
// self.taskDescriptionForSessionTasks其实就是manager地址
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
// 这是这里的关键---
// 将datatask和delegate建立关系, 以task.taskIdentifier作为key, delegate作为value, 存储到一个mutableTaskDelegatesKeyedByTaskIdentifier(manager的属性)字典中
[self setDelegate:delegate forTask:dataTask];
// 设置上传和下载进度块
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
问题: 为什么要使用AFNURLSessionManagerTaskDelegate?
使用一个全局的字段保存AFNURLSessionManagerTaskDelegate和task, 在session的代理方法中如果需要处理数据, 就通过task取出对应的AFNURLSessionManagerTaskDelegate, 调用AFNURLSessionManagerTaskDelegate代理的对应方法进行统一数据的处理.
4.4 各种回调
@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession;
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
各种回调, 供外界赋值, 会在代理方法中调用响应的回调, 将信息传递出去.
5.代理方法
在AFHTTPSessionManager
进行初始化的时候, 调用它的父类AFURLSessionManager
的初始化方法.
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
在AFURLSessionManager
中创建session
时, 设置了代理为AFURLSessionManager
, 所以NSURLSession
的相关代理是在AFURLSessionManager
中实现的.
我们来看一下AFHTTPSessionManager
实现的代理方法.
5.1 NSURLSessionDelegate
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
if (self.sessionDidBecomeInvalid) {
self.sessionDidBecomeInvalid(session, error);
}
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}
当前session
失效时, 代理调用此代理方法.
如果外界实现了sessionDidBecomeInvalid
的回调, 就会将当前session
和错误信息error
发送出去, 并且发送一个名字为AFURLSessionDidInvalidateNotification
通知出去, 用户可以监听这个通知.
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
// 默认方式处理
/*
NSURLSessionAuthChallengeUseCredential 使用指定证书
NSURLSessionAuthChallengePerformDefaultHandling 默认方式
NSURLSessionAuthChallengeCancelAuthenticationChallenge 取消挑战
*/
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
// sessionDidReceiveAuthenticationChallenge自定义的block, 外界可以通过set方法调用赋值, 自定义处理跳转
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
// 这里服务器要求客户端接收挑战的方式是NSURLAuthenticationMethodServerTrust
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 根据跳转的保护空间提供的信任 创建挑战证书
// 检查服务端是否可以信任(在authenticationMethod为NSURLAuthenticationMethodServerTrust的情况下)
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
// 创建挑战证书
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
当web服务器收到客户端的请求时, 有时候需要验证客户端用户是不是正常用户, 在决定是否返回真实数据, 这种情况成为客户端接收到挑战.
客户端根据服务器返回的challenge, 生成所需要的disposition(枚举类型, 应对挑战的方式)和credential(应对挑战生成的证书), 调用completionHandler将disposition和credential回应给服务器.
关于这部分的内容会在我的另一篇文章中做相关介绍, 传送门AFNetworking之AFSecurityPolicy深入学习.
5.2 NSURLSessionTaskDelegate
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler
{
NSURLRequest *redirectRequest = request;
if (self.taskWillPerformHTTPRedirection) {
redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
}
if (completionHandler) {
completionHandler(redirectRequest);
}
}
服务器重定向的时候会调用这个代理方法, 通过completionHandler
将重定向的request
传递给服务器.
如果用户实现了taskWillPerformHTTPRedirection
方法, 就将taskWillPerformHTTPRedirection
方法返回的重定向请求发送给服务器.
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.taskDidReceiveAuthenticationChallenge) {
disposition = self.taskDidReceiveAuthenticationChallenge(session, task, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
disposition = NSURLSessionAuthChallengeUseCredential;
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
同上, 这里不再做介绍.
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
{
NSInputStream *inputStream = nil;
if (self.taskNeedNewBodyStream) {
inputStream = self.taskNeedNewBodyStream(session, task);
} else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
inputStream = [task.originalRequest.HTTPBodyStream copy];
}
if (completionHandler) {
completionHandler(inputStream);
}
}
需要重新发送一个含有bodyStream的request给服务器的时候会调用调用此代理方法.
该方法会在下边两种情况下调用:
- task是由uploadTaskWithStramedRequest创建的, 那么在提供初始化的request body stram是会调用
- 因为认证挑战或者其他可恢复的服务器错误,而导致需要客户端重新发送一个含有body stream的request,这时候会调用
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
int64_t totalUnitCount = totalBytesExpectedToSend;
// 如果totalUnitCount获取失败, 就是用http header的Content-Length
if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
if(contentLength) {
totalUnitCount = (int64_t) [contentLength longLongValue];
}
}
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// 转发到自定义的delegate中
if (delegate) {
[delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
}
// 如果实现了自定义的block, 将数据传送出去, 例如做进度条的展示
if (self.taskDidSendBodyData) {
self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
}
}
周期性调用, 通知代理发送到服务器端数据的进度.
调用delegateForTask
方法获取和task
相关联的AFURLSessionManagerTaskDelegate
对象, 将任务转发到AFURLSessionManagerTaskDelegate
对象方法中做处理, 这个我们会在第6部分讲解.
这是AFN转发的第一个方法
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
// 根据task的taskIdentifier找delegate
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
// 完成以后, 从字典mutableTaskDelegatesKeyedByTaskIdentifier移除当前task
[self removeDelegateForTask:task];
}
// 调用block
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
数据传输完成的就会调用此代理方法, 无论是成功还是失败.
调用delegateForTask方法获取和task相关联的AFURLSessionManagerTaskDelegate对象, 将任务转发到AFURLSessionManagerTaskDelegate对象方法中做处理
这是AFN转发的第二个方法
5.3 NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
/*
NSURLSessionResponseCancel, dataTask会取消, 相当于调用了[task cancel]方法
NSURLSessionResponseAllow, dataTask正常运行
NSURLSessionResponseBecomeDownload, 变为downloadTask, 会调用URLSession:dataTask:didBecomeDownloadTask:方法
NSURLSessionResponseBecomeStream
*/
// 设置为请求继续进行
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
if (self.dataTaskDidReceiveResponse) {
disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
}
if (completionHandler) {
completionHandler(disposition);
}
}
接收到服务端的相应的时候调用此代理方法, 询问是否要将当前的dataTask
任务变成其他类型的任务, 如果不实现这个方法, 默认是任务继续执行.
在completionHandler
回调用需要传入NSURLSessionResponseDisposition
类型, 就是接下来要怎样处理dataTask
.
NSURLSessionResponseDisposition
是一个枚举类型:
- NSURLSessionResponseCancel, dataTask会取消, 相当于调用了[task cancel]方法
- NSURLSessionResponseAllow, dataTask正常运行
- NSURLSessionResponseBecomeDownload, 变为downloadTask, 会调用URLSession:dataTask:didBecomeDownloadTask:方法
- NSURLSessionResponseBecomeStream, 变为一个流任务.
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
if (delegate) {
// 将dataTask关联的delegate删除
[self removeDelegateForTask:dataTask];
// 为downloadTask关联delegate
[self setDelegate:delegate forTask:downloadTask];
}
if (self.dataTaskDidBecomeDownloadTask) {
self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
}
}
当dataTask
变成下载任务的时候就会调用这个代理方法.
根据dataTask
获取与其关联的delegate
, 并将dataTask
从mutableTaskDelegatesKeyedByTaskIdentifier
字典中移除, 将downloadTask
和delegate
做关联.
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
[delegate URLSession:session dataTask:dataTask didReceiveData:data];
if (self.dataTaskDidReceiveData) {
self.dataTaskDidReceiveData(session, dataTask, data);
}
}
接收到服务器的数据时会周期性调用此代理方法.
data
返回是自上一个调用以来接收到的数据, 所以我们要用一个容器
去存储这些data
数据.
AFN
将这个方法转发到AFURLSessionManagerTaskDelegate
对象中去处理, 将接收到的data
数据拼接到mutableData
中, 供后续处理.
这是AFN转发的第三个方法
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
{
NSCachedURLResponse *cachedResponse = proposedResponse;
if (self.dataTaskWillCacheResponse) {
cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
}
if (completionHandler) {
completionHandler(cachedResponse);
}
}
询问是否缓存response
响应, 当接收到所有的数据以后会调用此代理方法.
如果没有实现这个方法, 默认使用创建session时使用的configuration对象决定缓存策略.
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
if (self.didFinishEventsForBackgroundURLSession) {
dispatch_async(dispatch_get_main_queue(), ^{
self.didFinishEventsForBackgroundURLSession(session);
});
}
}
当一个后台任务完成, 并且你的app在后台状态, app会在后台自动重新运行, 并且调用app的UIApplicationDelegate
对象的-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
方法. 客户端应当存储completionHandler
回调, 并且在- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
方法中调用这个completionHandler
回调.
5.4 NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (self.downloadTaskDidFinishDownloading) {
// 自定义的block, 返回的是你想存储的文件地址路径
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
// 如果fileURl路径存在, 说明用户想把数据存储起来
if (fileURL) {
delegate.downloadFileURL = fileURL;
NSError *error = nil;
// 从临时路径移动到我们定义的路径, 将location位置的文件全部移动到自定义的路径fileURL处
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
// 如果移动文件失败, 就发送通知出去
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
}
return;
}
}
// 这里代用自定义代理的方法, 在自定义方法中做了上诉同样的操作, 是不是重复了???
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
当下载任务完成的时候回调用此代理方法.
由于这个location
地址是临时的, 所以必须将其移动到应用程序的沙箱容器目录的永久位置中, 才能读取.
如果要打开阅读这个文件, 应该在其他线程进行操作.
这是AFN转发的第四个方法
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
}
if (self.downloadTaskDidWriteData) {
self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
}
周期性调用此代理方法, 通知下载进度.
bytesWritten
:从上次调用这个代理方法以后接收到的数据
totalBytesWritten
:接收到数据的总字节数
totalBytesExpectedToWrite
:期望接收到的数据字节总数, 有header中的Content-Length提供, 如果没有提供的话 默认是NSURLSessionTransferSizeUnknown
将数据转发到AFURLSessionManagerTaskDelegate
代理中.
这是AFN转发的第五个方法
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didResumeAtOffset:fileOffset expectedTotalBytes:expectedTotalBytes];
}
if (self.downloadTaskDidResume) {
self.downloadTaskDidResume(session, downloadTask, fileOffset, expectedTotalBytes);
}
}
当下载任务重新开始时, 会调用此代理方法.
如果断点续传的下载任务被取消或者失败了, 你可以请求一个resumeData对象, 这个对象包含了足够的信息去重新开始下载任务.
你可以调用downloadTaskWithResumeData:方法或者downloadTaskWithResumeData:completionHandler:, 将resumeData作为方法的参数.
当你调用这两个方法时, 你会得到一个新的下载任务, 如果你重新开这个下载任务, 那么就会调用URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:代理方法, 意味着下载重新开始了.
这是AFN转发的第六个方法
至此, AFN中实现的代理方法已经全部描述完毕.
6. AFURLSessionManagerTaskDelegate中的方法
AFURLSessionManagerTaskDelegate
中的方法是有AFURLSessionManager
中实现的代理方法转发过来的. 在AFURLSessionManagerTaskDelegate
的这些方法中做统一的处理.
6.1 NSURLSessionTaskDelegate
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
// 强引用, 防止被提前释放, 因为AFURLSessionManager强引用delegate, delegate有一个弱引用属性manager.
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
// 创建一个userInfo字典, 存储信息, 这里创建的userInfo会通过通知发送出去.
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
//Performance Improvement from #2672
// 这里也是值得我们学习地方, mutableData存储的数据我们用一个局部变量保存, 并且清空mutableData, 在这个方法结束以后局部变量被清空, mutableData也指向nil.
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;
}
// 数据存储的位置
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
// 如果有错误信息, 处理错误信息
if (error) {
// 同样将错误信息存在userInfo中, 通知发送出去.
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
// 使用group处理, 当所有的task任务都完成, 才会发送通知(个人理解为, AFN中并没有监听这个队列组, 是为了让使用者通过监听队列组)
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(), ^{
// 将userInfo通过通知信息发出去
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
} else {
// 并发队列异步线程解析数据, 不阻塞线程
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
// 解析数据, 在AFURLResponseSerialization中解析, AFJSONResponseSerializer将数据解析成 json 格式
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
// 注意: 如果存在downloadFileURL, 证明已经将数据存储到了磁盘上了, 所以此处的responseObject存储的是data存放的位置, 将responseObject通过completionHandler传出去.
if (self.downloadFileURL) {
responseObject = self.downloadFileURL;
}
// 如果responseObject不为空, 就存储到userInfo字典中, 会通过通知发送出去
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
// 如果解析错误, 将错误信息, 存储到userInfo字典中, 会通过通知发送出去
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];
});
});
});
}
}
task完成以后会调用此代理方法, 无论成功还是失败都会调用.
url_session_manager_completion_group()
是一个创建队列组单例的函数.
解析data数据会调用AFJSONResponseSerializer
的responseObjectForResponse
方法将data
数据解析成responseObject
, 关于AFJSONResponseSerializer
我会在另外一篇文章讲解.
部分代码的功能已经在代码中注释.
6.2 NSURLSessionDataDelegate
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
dataTask:(NSData *)data
{
self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
// 将接受到的数据拼接到mutableData中, 在接受数据完成的方法中, 操作mutableData解析即可.
[self.mutableData appendData:data];
}
此方法在AFURLSessionManager的URLSession:dataTask:dataTask:方法中调用.
将data
数据拼接到mutableData
中, 并且根据dataTask
设置downloadProgress
的进度.
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
self.uploadProgress.completedUnitCount = task.countOfBytesSent;
}
此方法主要记录上传进度.
6.3 NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
self.downloadProgress.totalUnitCount = totalBytesExpectedToWrite;
self.downloadProgress.completedUnitCount = totalBytesWritten;
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
self.downloadProgress.totalUnitCount = expectedTotalBytes;
self.downloadProgress.completedUnitCount = fileOffset;
}
这两个方法主要记录下载的进度.
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
self.downloadFileURL = nil;
if (self.downloadTaskDidFinishDownloading) {
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (self.downloadFileURL) {
NSError *fileManagerError = nil;
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
}
}
}
}
将下载文件移动到指定的下载位置.