比如我们发生一个GET
请求时,会调用到如下代码:
__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);
}
}
}];
该方法就是使用指定的请求去创建一个NSURLSessionDataTask
,那么内部是如何实现的呢?
** AFURLSessionManager ** 基于指定的 NSURLSessionConfiguration
创建和管理了一个 NSURLSession
,并遵循了NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate 这四个协议。
NSURLSession & NSURLSessionTask Delegate 方法
AFURLSessionManager
实现了以下代理方法:
NSURLSessionDelegate
URLSession:didBecomeInvalidWithError:
URLSession:didReceiveChallenge:completionHandler:
URLSessionDidFinishEventsForBackgroundURLSession:
NSURLSessionTaskDelegate
URLSession:willPerformHTTPRedirection:newRequest:completionHandler:
URLSession:task:didReceiveChallenge:completionHandler:
URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
URLSession:task:needNewBodyStream:
URLSession:task:didCompleteWithError:
NSURLSessionDataDelegate
URLSession:dataTask:didReceiveResponse:completionHandler:
URLSession:dataTask:didBecomeDownloadTask:
URLSession:dataTask:didReceiveData:
URLSession:dataTask:willCacheResponse:completionHandler:
NSURLSessionDownloadDelegate
URLSession:downloadTask:didFinishDownloadingToURL:
URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
AFURLSessionManager 包含如下属性:
-
NSURLSession *session;
创建的NSURLSession对象 -
NSOperationQueue *operationQueue;
回调队列-
id
服务器返回的响应序列化responseSerializer; -
AFSecurityPolicy *securityPolicy;
安全政策 -
AFNetworkReachabilityManager *reachabilityManager;
监测网络的可达性 -
NSArray
当前session下的所以任务组*tasks; -
NSArray
当前正在执行datatask数组*dataTasks; -
NSArray
当前正在执行上传task数组*uploadTasks -
NSArray
当前正在执行的下载task数组*downloadTasks; -
dispatch_queue_t completionQueue;
The dispatch queue forcompletionBlock
-
dispatch_group_t completionGroup;
The dispatch group forcompletionBlock
-
BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
是否尝试重试后台会话的上载任务的创建
-
我们从初始化方法开始分析:
- (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; //设置最大并发数为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];
//添加锁,确保mutableTaskDelegatesKeyedByTaskIdentifier读写线程安全
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
//为每个task设置代码块,同时也保证后台`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 {
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
//根据请求创建task
dataTask = [self.session dataTaskWithRequest:request];
});
//会初始化`AFURLSessionManagerTaskDelegate`对象,添加对应的下载进度,下载完成回调,设置代理,线程安全的根据`task.taskIdentifier`添加delegate,并通过AFNSURLSessionTaskDidResumeNotification和AFNSURLSessionTaskDidSuspendNotification设置暂停和恢复的通知方法。也就是说`mutableTaskDelegatesKeyedByTaskIdentifier `通过key为`taskIdentifier `来管理对应的task。
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
然后返回dataTask,并且启动[dataTask resume];
接下来便是启动下载任务执行对应的回调方法,然后在相应的方法中通过block进行通信。
NSURLSessionDelegate
- ``NSURLSessionTaskDelegate`
NSURLSessionDataDelegate
- ``NSURLSessionDownloadDelegate`
举个:
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
//当当前Sesssion由于错误原因不可用时则会调用sessionDidBecomeInvalid进行通知
if (self.sessionDidBecomeInvalid) {
self.sessionDidBecomeInvalid(session, error);
}
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}
@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
//当访问HTTPS的路径时会调用该方法,处理服务器返回的证书
//NSURLAuthenticationChallenge : 授权质问
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
//sessionDidReceiveAuthenticationChallenge存在则返回当前证书信任类型
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
//从服务器返回的受保护空间中拿到证书的类型,判断是否是受信任的,这里涉及到`AFSecurityPolicy`
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
//如果服务端返回的证书是可信任的,则创建一个指定握手是可信的NSURLCredential,并返回证书处理方式
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}dReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
#pragma mark - NSURLSessionTaskDelegate
//HTTP请求尝试对另一个URL执行重定向时调用
- (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);
}
}
//当一个session task需要发送一个新的request body stream到服务器端的时候,调用该代理方法。
- (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);
}
}
//定期发送通知通知上传进度,这些信息也可作为任务的属性
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
int64_t totalUnitCount = totalBytesExpectedToSend;
if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
//读取出回调的总字节数,如果为NSURLSessionTransferSizeUnknown,则使用HTTP header中的Content-Length作为totalUnitCount
NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
if(contentLength) {
totalUnitCount = (int64_t) [contentLength longLongValue];
}
}
//根据task.taskIdentifier线程安全的从mutableTaskDelegatesKeyedByTaskIdentifier中取出AFURLSessionManagerTaskDelegate
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
if (delegate) {
//delegate存在则执行该方法为self.uploadProgress的totalUnitCount和completedUnitCount赋值,通知总共要发送多少,已经发送了多少,将该代理方法转发到自定义的delegate中
[delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
}
//同时在taskDidSendBodyData中把上传进度等回调
if (self.taskDidSendBodyData) {
self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
}
}
//作为与特定任务相关的最后一条消息发送。错误可能为nil,这意味着此任务完成没有发生错误
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
//在后台完成任务时,委托可能为nil
if (delegate) {
//将该方法转发给AFURLSessionManagerTaskDelegate类
[delegate URLSession:session task:task didCompleteWithError:error];
//根据task.taskIdentifier从mutableTaskDelegatesKeyedByTaskIdentifier线程安全的移除delegate
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
我们来看下AF内部在回调完成做了什么:
#pragma mark - NSURLSessionTaskDelegate
//第186行
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
//因为self.manager使用的weak属性修饰符,为了防止中途释放__strong一次
__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;
}
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
if (error) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
//处理错误,如果completionGroup不存在则使用私有的url_session_manager_completion_group,completionQueue为空则使用dispatch_get_main_queue(),去异步更新AFURLSessionTaskCompletionHandler
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(), ^{
//根据对应的task和data将response根据AFHTTPResponseSerializer解析成可用的数据格式
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 mark - NSURLSessionDataDelegate
//任务已收到响应并且直到完成块被调用时不会收到消息
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
if (self.dataTaskDidReceiveResponse) {
disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
}
if (completionHandler) {
completionHandler(disposition);
}
}
//如下的几个代理方法都是类似的,`didBecomeDownloadTask` `didReceiveData` `willCacheResponse` 都是接受数据之后使用block将其进行回调通知
//如果接收到了-application:handleEventsForBackgroundURLSession:completionHandler:消息,这就意味着之前这个session中已经入队的所有消息都转发出去了,此时调用以前存储的completion handler是安全的,也可以启动任何导致completion handler处理程序的内部更新
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
if (self.didFinishEventsForBackgroundURLSession) {
dispatch_async(dispatch_get_main_queue(), ^{
self.didFinishEventsForBackgroundURLSession(session);
});
}
}
#pragma mark - NSURLSessionDownloadDelegate
//当完成下载任务时调用。委托应该从指定位置复制或移动文件到一个新位置,因为当委托消息返回时,该文件将被删除。
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (self.downloadTaskDidFinishDownloading) {
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (fileURL) {
delegate.downloadFileURL = fileURL;
NSError *error = nil;
//如果移动文件到指定位置失败则发送对应通知
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
}
return;
}
}
if (delegate) {
//delegate去转发给实现的代理方法
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
对应的这两个方法的功能也是类似的:
//定期发送通知下载进度的委托
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
//用于断点续传,当下载任务恢复时调用,如果下载因为错误失败,那么userInfo字典将包含一个NSURLSessionDownloadTaskResumeData的key,value是恢复的数据
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
网络请求的问题解决了,那如何进行下载和暂停呢,换句话说断点续传是如何控制的呢?之前在第697行有个方法,- (void)addNotificationObserverForTask:(NSURLSessionTask *)task
该方法是注册通知的形式,如果需要调用则需要发送AFNSURLSessionTaskDidResumeNotification
和AFNSURLSessionTaskDidSuspendNotification
,那我们顺藤摸瓜来找下,发现在**_AFURLSessionTaskSwizzling **中有我们想要的[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
_AFURLSessionTaskSwizzling
+ load
作为 Objective-C 中的一个方法,与其它方法有很大的不同。它只是一个在整个文件被加载到运行时,在 main 函数调用之前被 ObjC 运行时调用的钩子方法,load 会在类或分类被添加到runtime
时调用。
+ (void)load {
if (NSClassFromString(@"NSURLSessionTask")) {
/**
Some Assumptions:
- No implementations of `resume` or `suspend` call super. If this were to change in a future version of iOS, we'd need to handle it.
- No background task classes override `resume` or `suspend`
**/
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
//通过`NSURLSession`来创建一个NSURLSessionDataTask实例
NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
//获取`af_resume`的方法实现的指针
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
Class currentClass = [localDataTask class];
//检查当前类是否实现了`resume`
while (class_getInstanceMethod(currentClass, @selector(resume))) {
//如果实现了。则获取当前类的父类
Class superClass = [currentClass superclass];
//获取当前实现`resume`的当前类指针
IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
//获取当前实现`resume`的父类指针
IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
//如果当前类实现的`resume`和父类实现的`resume`不相等 && 当前类实现的`resume`和`af_resume`实现也不相等则交换方法
if (classResumeIMP != superclassResumeIMP &&
originalAFResumeIMP != classResumeIMP) {
[self swizzleResumeAndSuspendMethodForClass:currentClass];
}
//设置当前类为当前类的父类,重复以上步骤
currentClass = [currentClass superclass];
}
[localDataTask cancel];
[session finishTasksAndInvalidate];
}AFHTTPResponseSerializer
}
- (void)af_resume {
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
[self af_resume];
//这里需要说明一下,因为之前已经调用`method_exchangeImplementations`对换了`resume`方法和`af_resume`,所以该处调用[self af_resume]实际上就是调用系统的resume方法
if (state != NSURLSessionTaskStateRunning) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
}
}