AFNetworking 3.0源码阅读 - AFURLSessionManager

这次来说一下AFURLSessionManager

从头文件的英文注释可以看出AFURLSessionManager类创建并管理着NSURLSession对象,而NSURLSession又是基于NSURLSessionConfiguration的。同时该类也是AFHTTPSessionManager的父类,下一篇来讲。

AFURLSessionManager实现了四个协议

1.NSURLSessionDelegate

  • URLSession:didBecomeInvalidWithError:
  • URLSession:didReceiveChallenge:completionHandler:
  • URLSessionDidFinishEventsForBackgroundURLSession:

2.NSURLSessionTaskDelegate

  • URLSession:willPerformHTTPRedirection:newRequest:completionHandler:
  • URLSession:task:didReceiveChallenge:completionHandler:
  • URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
  • URLSession:task:needNewBodyStream:
  • URLSession:task:didCompleteWithError:

3.NSURLSessionDataDelegate

  • URLSession:dataTask:didReceiveResponse:completionHandler:
  • URLSession:dataTask:didBecomeDownloadTask:
  • URLSession:dataTask:didReceiveData:
  • URLSession:dataTask:willCacheResponse:completionHandler:

4.NSURLSessionDownloadDelegate

  • URLSession:downloadTask:didFinishDownloadingToURL:
  • URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:
  • URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:

以上协议方法当子类需要重写时必须在方法内调用一次对应的[supe method]。

上面说到该类是创建并管理NSURLSession,NSURLSession相关的东西可以查阅文档但是这里要说一下

文档中在这里强调,session对象和他的delegate是强引用关系,在涉及内存方面的时候需要注意。

AFURLSessionManager的.h文件

一个实现了AFURLResponseSerialization的对象也就是上一篇讲到的那些,默认是AFJSONResponseSerializer。该属性不可以为nil。

安全策略,若没有特殊指定使用defaultPolicy。

网络可达性,使用sharedManager。

分别是包含了所有task的数组,包含了dataTask的数组,包含了uploadTask的数组以及包含了downloadTask的数组。在这里要说下这几中task的关系和作用:

  • NSURLSessionTask是NSURLSessionDataTask和NSURLSessionDownloadTask的父类,而NSURLSessionDataTask是NSURLSessionUploadTask的父类,  也就是说NSURLSessionTask是Top Level。
  • DataTask,常见的网络请求,如GET/POST。不支持后台。
  • UploadTask,在处理上传请求时有优势。
  • DownloadTask,在处理下载请求时有优势。
  • DataTask和DownloadTask均可以有下载操作,但是前者是通过读取response后生成的NSData对象,在借由NSData对象生成相应文件;而后者直接下载源文件到磁盘中。

请求成功后的block在该队列上执行,如果不指定就使用主队列。

请求成功后的block在该组上执行,如果不指定就使用一个私有的组。

是否尝试在初始调用返回‘ nil ‘时为后台会话重试创建上传任务。默认情况下“不”。这是一个为了解决ios7中在创建后台上传任务时,有时会返回nil问题的一个属性。如果你又后台上传的一些情景请设置该属性为yes。

以上是一些属性的定义,接下来看看方法的定义。

初始化方法,注意该方法后面有一个宏,这个宏作用是希望调用者使用该方法初始化。

废弃掉session对象取消tasks,为什么要废弃掉session呢。原因是防止上面提到的那个session和delegate强引用关系造成的内存泄露。

接下来是dataTask相关的方法。

创建NSURLSessionDataTask

创建NSURLSessionUploadTask,上传本地文件。

创建NSURLSessionUploadTask,通过HTTP body上传二进制数据。

创建NSURLSessionUploadTask,通过数据流上传数据。

创建NSURLSessionDownloadTask,根据请求下载。

创建NSURLSessionDownloadTask,该task恢复一个取消的或者失败的下载会话。至于这个resumeData是怎么获得的,这里借用一段话的截图说明。

获取上传和下载进度。

设置block回调,还有一些block设置的方法及不罗列出来了。然后还有一些通知的定义贴在下面

/**
 Posted when a task resumes.
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidResumeNotification;

/**
 Posted when a task finishes executing. Includes a userInfo dictionary with additional information about the task.
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteNotification;

/**
 Posted when a task suspends its execution.
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidSuspendNotification;

/**
 Posted when a session is invalidated.
 */
FOUNDATION_EXPORT NSString * const AFURLSessionDidInvalidateNotification;

/**
 Posted when a session download task encountered an error when moving the temporary download file to a specified destination.
 */
FOUNDATION_EXPORT NSString * const AFURLSessionDownloadTaskDidFailToMoveFileNotification;

/**
 The raw response data of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if response data exists for the task.
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseDataKey;

/**
 The serialized response object of the task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the response was serialized.
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteSerializedResponseKey;

/**
 The response serializer used to serialize the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if the task has an associated response serializer.
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteResponseSerializerKey;

/**
 The file path associated with the download task. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an the response data has been stored directly to disk.
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteAssetPathKey;

/**
 Any error associated with the task, or the serialization of the response. Included in the userInfo dictionary of the `AFNetworkingTaskDidCompleteNotification` if an error exists.
 */
FOUNDATION_EXPORT NSString * const AFNetworkingTaskDidCompleteErrorKey;

另外头文件中还出现了一对宏NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END,在对宏之间的简单指针对象会被假定为nonnull也就是不可以为NULL或者NIL。如果对象需要为这两种就要指定用__nullable。

AFURLSessionManager的.m文件

浏览下整个实现文件,可以看到文件中有三个类AFURLSessionManagerTaskDelegate,_AFURLSessionTaskSwizzling和AFURLSessionManager。一个一个来看。

AFURLSessionManagerTaskDelegate

该类遵循NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate三种协议。

初始化方法和销毁方法。

kvo观察方法。

以下是协议方法

- NSURLSessionTaskDelegate

//task完成回调
- (void)URLSession:(__unused NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    __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;
    }

    //是否有downloadFileURL
    if (self.downloadFileURL) {
        userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
    } else if (data) {
        userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
    }

    //是否包含错误信息
    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];
                });
            });
        });
    }
}

- NSURLSessionDataDelegate

//dataTask接收数据
- (void)URLSession:(__unused NSURLSession *)session
          dataTask:(__unused NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{
    self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
    self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;

    [self.mutableData appendData:data];
}

//dataTask上传数据
- (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;
}

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

_AFURLSessionTaskSwizzling

_AFURLSessionTaskSwizzling是用来解决早期版本(2.x)中使用kvo机制监听NSURLSessionTask的state后某些情况导致的crash。主要是因为在iOS个版本中NSURLSessionTask的继承链不同导致的,这里放一个注释翻译的截图

然后再一个一个函数看??。

//此处确保交换和管理
+ (void)load {
    /**
     WARNING: Trouble Ahead
     https://github.com/AFNetworking/AFNetworking/pull/2702
     */

    if (NSClassFromString(@"NSURLSessionTask")) {

        //初始化一个session
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
        NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
        NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
        //需要交换的恢复方法的实现
        IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
        //当前task的类
        Class currentClass = [localDataTask class];

        //当前类有resume方法,进入while内部
        while (class_getInstanceMethod(currentClass, @selector(resume))) {
            Class superClass = [currentClass superclass];
            //当前类和父类的resume方法实现
            IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
            IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
            //当前类和父类的resume方法实现不同或者准备交换的方法与当前存在的方法实现不同进行方法交换
            if (classResumeIMP != superclassResumeIMP &&
                originalAFResumeIMP != classResumeIMP) {
                [self swizzleResumeAndSuspendMethodForClass:currentClass];
            }
            currentClass = [currentClass superclass];
        }

        //取消task,session失效
        [localDataTask cancel];
        [session finishTasksAndInvalidate];
    }
}

在load方法中进行交换和管理,确保一定执行。

//调用交换
+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
    Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
    Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));

    if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
        af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
    }

    if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
        af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
    }
}

- (NSURLSessionTaskState)state {
    NSAssert(NO, @"State method should never be called in the actual dummy class");
    return NSURLSessionTaskStateCanceling;
}

//需要交换的恢复方法
- (void)af_resume {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_resume];

    if (state != NSURLSessionTaskStateRunning) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
    }
}

//需要交换的暂停方法
- (void)af_suspend {
    NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
    NSURLSessionTaskState state = [self state];
    [self af_suspend];

    if (state != NSURLSessionTaskStateSuspended) {
        [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
    }
}

相关方法。

定义的内联函数。

AFURLSessionManager

新增的属性。

初始化方法。

这里的两个回调监听的是AFNSURLSessionTaskDidResumeNotification这个通知,而这个通知的发送位置也就是第二部分提到的交换的方法里面。

设置task的代理和获取task的代理,这里有加锁操作。

分别为三种task添加delegate以及移除方法。

该方法是取消task,无效session时调用。这里要说下里面调用的两个方法:

  • invalidateAndCancel:取消所有正在执行的任务并使session失效。
  • finishTasksAndInvalidate:允许任务继续执行直至任务完成后使session失效。

这两个方法有着共同的说明:一旦session失效,所关联的delegate和callback都是断开,该session无法恢复。同时在文档中也有一个注意点。

使用sharedSession获取的会话在调用这两个方法时是没效果的。

下面开始说创建dataTask相关的方法实现了。

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler: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;
    //这里通过在串行队列中创建task来解决在iOS8以下偶尔发生的因并发创建task导致的identifier不唯一的问题
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });

    //设置task的代理
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}

这里有一个url_session_manager_create_task_safely函数,函数的实现是这样的

大体上来说就是创建一个串行队列同步执行block内的内容,block里是系统创建task的方法。如果当你想定义一个无返回值的block的时候可以直接使用dispatch_block_t,写法上更方便一些。

//通过文件url创建上传任务
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromFile:(NSURL *)fileURL
                                         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 fromFile:fileURL];

        // uploadTask may be nil on iOS7 because uploadTaskWithRequest:fromFile: may return nil despite being documented as nonnull (https://devforums.apple.com/message/926113#926113)
        //若没成功创建出上传任务且重新创建的属性为真,session配置标识存在
        if (!uploadTask && self.attemptsToRecreateUploadTasksForBackgroundSessions && self.session.configuration.identifier) {
            for (NSUInteger attempts = 0; !uploadTask && attempts < AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask; attempts++) {
                uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
            }
        }
    });

    if (uploadTask) {
        [self addDelegateForUploadTask:uploadTask
                              progress:uploadProgressBlock
                     completionHandler:completionHandler];
    }

    return uploadTask;
}

//通过NSData创建上传任务
- (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;
}

//通过流创建上传任务
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
                                                 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 uploadTaskWithStreamedRequest:request];
    });

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

    return uploadTask;
}

三种上传任务的创建方法,实现大同小异。

//通过请求下载
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                             progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                          destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                    completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
    __block NSURLSessionDownloadTask *downloadTask = nil;
    url_session_manager_create_task_safely(^{
        downloadTask = [self.session downloadTaskWithRequest:request];
    });

    [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];

    return downloadTask;
}

//通过resumeData下载
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
                                                progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                             destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                       completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
    __block NSURLSessionDownloadTask *downloadTask = nil;
    url_session_manager_create_task_safely(^{
        downloadTask = [self.session downloadTaskWithResumeData:resumeData];
    });

    [self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];

    return downloadTask;
}

两种下载任务的创建。

最后一块,各种代理的实现。

NSURLSessionDelegate

代理1

//session失效时被调用
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
    if (self.sessionDidBecomeInvalid) {
        self.sessionDidBecomeInvalid(session, error);
    }

    //发送通知
    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}

代理2

/**
 * 用于身份验证,只要为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) {
        disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
    } else {
        //判断证书是否被信任
        if ([challenge.protectionSpace.authenticationMethod isEqualToString: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);
    }
}

NSURLSessionTaskDelegate

代理3

//HTTP重定向时调用
- (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);
    }
}

代理4

//task层的身份验证
- (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);
    }
}

和session层的实现无二。

代理5

//上传任务是通过流来创建时调用,提供请求所需的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)]) {
        //拷贝一份HTTPBodyStream
        inputStream = [task.originalRequest.HTTPBodyStream copy];
    }

    if (completionHandler) {
        completionHandler(inputStream);
    }
}

在之前的源码阅读AFURLRequestSerialization里是有猜测为何AFMultipartBodyStream作为HTTPBodyStream的内容没有相应open操作可能是因为在代理中传给系统层进行操作的原因就在这。

文件和NSData对象作为body时不需要实现该方法。

代理6

/**
 * 上传进度回调
 * bytesSent 已发送字节
 * totalBytesSent 总字节
 * totalBytesExpectedToSend 预期发送的总字节
 */
- (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) {
        NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
        if(contentLength) {
            totalUnitCount = (int64_t) [contentLength longLongValue];
        }
    }

    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];

    if (delegate) {
        [delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
    }

    if (self.taskDidSendBodyData) {
        self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
    }
}

代理7

//完成数据传输回调
- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    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];

        //完成后移除delegate和task的绑定
        [self removeDelegateForTask:task];
    }

    if (self.taskDidComplete) {
        self.taskDidComplete(session, task, error);
    }
}

NSURLSessionDataDelegate

代理8

//接收到响应的回调
- (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);
    }
}

代理9

//data task 转为 download task 的回调
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
    AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
    if (delegate) {
        //重新绑定
        [self removeDelegateForTask:dataTask];
        [self setDelegate:delegate forTask:downloadTask];
    }

    if (self.dataTaskDidBecomeDownloadTask) {
        self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
    }
}

代理10

//接收到NSData的回调
- (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);
    }
}

代理11

//将要缓存响应的回调
- (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);
    }
}

这里回调能够触发是有规则的

1.支持cache的传输协议。

2.请求成功。

3.提供的响应来自服务器,非cache。

4.请求缓存策略支持缓存。

5.响应大小足够小,可以合理地适应缓存。(例如,如果提供磁盘缓存,响应必须不大于磁盘缓存大小的5%)

代理12

//后台传输任务完成回调,并且应用没在前台
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    if (self.didFinishEventsForBackgroundURLSession) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.didFinishEventsForBackgroundURLSession(session);
        });
    }
}

主要过程在文档中的机翻。大体上就是完成了会调用AppDelegate的handleEventsForBackgroundURLSession:completionHandler: message,在该方法中创建新的NSURLSessionConfiguration和NSURLSession对象触发该代理回调,在回调中进行UI刷新等操作后调用completionHandler()通知系统。

同时因为可能有对UIKit相关操作,所以要放到主线程。

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

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

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

补充

AFURLSessionManager中代理的回调一般都被处理成外界传进来的block实现,所以代理方法的实现与否就变成了对应的block是否为nil,所以重写了这个方法。

以上。

原文地址:https://www.cnblogs.com/kaisi/p/9479597.html

时间: 2024-11-10 08:51:46

AFNetworking 3.0源码阅读 - AFURLSessionManager的相关文章

AFNetworking 3.0源码阅读 - AFURLRequestSerialization

AFURLRequestSerialization模块主要做的两样事情: 1.创建普通NSMUtableURLRequest请求对象2.创建multipart NSMutableURLRequest请求对象此外还有比如:处理查询的URL参数 也就是说这主要实现了请求报文构造器的功能 在AFURLRequestSerialization.h文件中声明了三个类来帮助开发者构造请求报文 1.AFHTTPRequestSerializer 2.AFJSONRequestSerializer 3.AFPr

AFNetworking 3.0 源码解读(四)之 AFURLResponseSerialization

本篇是AFNetworking 3.0 源码解读的第四篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization 这次主要讲AFURLResponseSerialization(HTTP响应)这一个类的知识. 这是一个协议,只要遵守这个协议,就要实现N

muduo2.0源码阅读记录

花了20天的时间读了陈硕先生的<Linux多线程服务端编程>一书的前8章.当然,每天阅读的时间并不算多,中间有些部分也反反复复看了几遍,最后也算是能勉强接受作者传授的知识.配合书把muduo2.0网络部分的代码和日志库代码细读了一遍,这也算是个人第一次较为深入地去读取一个开源项目源码.通过书和源码的阅读,确实是对不少东西加深了理解. 本来想按自己的理解来写源码阅读笔记的,但考虑到网上关于muduo代码的解析文章已经很多并且写的很好了,就放弃了这个想法.摘录几个自己在源码阅读过程中参考的网页:

seajs 3.0.0 源码阅读笔记

写笔记的时候才注意到我看的源代码是 3.0.0 的,但是官方发布的最新版本是 2.3.0.相信大部分是相同的,所以先把这个记完,再看一次 2.3.0 的代码. seajs 的源代码可以在 github上获取.seajs 在文档"如何参与开发"中说明了阅读顺序,当然为了便于阅读,在了解了目录结构之后,我直接阅读了合并好的 sea-debug.js. 整个seajs采用的是2空格缩进,避免分号的写法,我不是很习惯,但不影响阅读. 文件/目录结构 文档中各个源文件所包含的内容大致如下: in

AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager

做ios开发,AFNetworking 这个网络框架肯定都非常熟悉,也许我们平时只使用了它的部分功能,而且我们对它的实现原理并不是很清楚,就好像总是有一团迷雾在眼前一样. 接下来我们就非常详细的来读一读这个框架的代码,我们的目标就是理解了它的思想之后,能够明白我们的请求是如何实现的,我们的代码哪里还需要进行改进,如果能够更进一步,我们能够总结出一套适合大部分应用的网络架构思想. 能够让一些人从中受益. 我们先来看看整个框架的文件系统,我们先不对每个文件的作用进行说明,在整个源码解读最后的一篇中我

AfNetworking 3.0源码解读

做ios开发,AFNetworking 这个网络框架肯定都非常熟悉,也许我们平时只使用了它的部分功能,而且我们对它的实现原理并不是很清楚,就好像总是有一团迷雾在眼前一样. 接下来我们就非常详细的来读一读这个框架的代码,我们的目标就是理解了它的思想之后,能够明白我们的请求是如何实现的,我们的代码哪里还需要进行改进,如果能够更进一步,我们能够总结出一套适合大部分应用的网络架构思想. 能够让一些人从中受益. 我们先来看看整个框架的文件系统,我们先不对每个文件的作用进行说明,在整个源码解读最后的一篇中我

AFNetworking 3.0 源码解读 总结

终于写完了 AFNetworking 的源码解读.这一过程耗时数天.当我回过头又重头到尾的读了一篇,又有所收获.不禁让我想起了当初上学时的种种情景.我们应该对知识进行反复的记忆和理解.下边是我总结的 AFNetworking 中能够学到的知识点. 1.枚举(enum) 使用原则:当满足一个有限的并具有统一主题的集合的时候,我们就考虑使用枚举.这在很多框架中都验证了这个原则.最重要的是能够增加程序的可读性. 示例代码: /** * 网络类型 (需要封装为一个自己的枚举) */ typedef NS

Vue2.0源码阅读笔记--生命周期

一.Vue2.0的生命周期 Vue2.0的整个生命周期有八个:分别是 1.beforeCreate,2.created,3.beforeMount,4.mounted,5.beforeUpdate,6.updated,7.beforeDestroy,8.destroyed. 用官方的一张图就可以清晰的了解整个生命周期: Vue最新源码下载:地址 二:源码分析 1.先看new Vue实例的方法 创建Vue实例的文件是: src/core/instance/index.js function Vue

ios AFNetworking 3.0 原码阅读分析 (一)(AFURLRequestSerialization)

本文主要内容是讲AFNetworking中的AFURLRequestSerialization.它主要的作用是在我们要发送一个网络请求的时候帮助我们创NSMutableURLRequest并封装好所需要的参数到NSMutableURLRequest中.那它内部做了些什么,提供了什么功能,使得我们进行网络请求时候变得如此方便.简单.好像我们什么都不用管就能建立一个正确的请求体NSURLRequest.接下来就会一步步揭开它神秘的面纱. 概览 首先看一下在AFURLRequestSerializat