美文网首页第三方框架学习AFNetworkingiOS奋斗
AFNetworking 3.0 源码解析之NSURLSessi

AFNetworking 3.0 源码解析之NSURLSessi

作者: SemyonXu | 来源:发表于2016-10-07 21:40 被阅读827次

    NSURLSession是AFNetworking的核心部分,主要负责网络通信部分。

    下面分两个部分来解析:
    父类:AFURLSessionManager,
    子类:AFHTTPSessionManager

    父类AFURLSessionManager对系统的NSURLSession类的网络请求进行了封装,并把NSURLSession里面的delegate返回值通过block返回回去。
    子类AFHTTPSessionManager继承了所有父类的功能,在这个基础上封装了支持GET,POST,PUT,HEAD,PATCH,DELETE等HTTP请求方式,使用更加方便。当然不用子类,也是可以直接进行网络请求的。

    AFURLSessionManager

    先看初始化方法

      - (instancetype)init {
        return [self initWithSessionConfiguration:nil];
    }
    
    - (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];
    
        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;
    }
    

    这里对各个属性进行了基本的初始化,为session初始化:

    self.session = [NSURLSessionsessionWithConfiguration:self.sessionConfigurationdelegate:selfdelegateQueue:self.operationQueue];
    

    我们可以看到是用的系统NSURLSession的初始化方法。

    此处会遍历所有task添加delegate方法,猜测是兼容上次为未完成的请求Task,再继续进行处理,具体还得看情况,因为我跟了多次都是没有走这个方法。

    网络请求支持的几个方法

    - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                                completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))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;
    
    ///---------------------------
    /// @name Running Upload Tasks
    ///---------------------------
    
    - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                             fromFile:(NSURL *)fileURL
                                             progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
                                    completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError  * _Nullable error))completionHandler;
    
    - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                             fromData:(nullable NSData *)bodyData
                                             progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
                                    completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
    
    - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
                                                     progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
                                            completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
    
    ///-----------------------------
    /// @name Running Download Tasks
    ///-----------------------------
    
    - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                                 progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
                                              destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                        completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
    
    - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
                                                    progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
                                                 destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                           completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
    

    这里面封装了系统NSURLSession的dataTask、uploadTask、downloadTask方法。而且另外添加了数据的进度progress。

    下面我们来解析一下实现原理:
    首先看一下dataTask的请求:

    - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                                   uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                                 downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                                completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler {
    
        __block NSURLSessionDataTask *dataTask = nil;
        url_session_manager_create_task_safely(^{
            dataTask = [self.session dataTaskWithRequest:request]; // 核心方法,NSURLSessionDataTask的dataTask方法。
        });
    
        [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler]; // 设置代理,所有进度的处理都在代理中。进度的回调。
    
        return dataTask;
    }
    

    我们可以看到,这里使用的是NSURLSessionDataTask的dataTaskWithRequest:方法,这个地方加了数据保护。然后就是为这个dataTask添加代理。所以核心的工作都交个这个代理AFURLSessionManagerTaskDelegate类来处理,那么这个代理的作用就显得尤为重要。

    网络请求的代理类AFURLSessionManagerTaskDelegate

    @interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
    @property (nonatomic, weak) AFURLSessionManager *manager;
    @property (nonatomic, strong) NSMutableData *mutableData;
    @property (nonatomic, strong) NSProgress *uploadProgress;
    @property (nonatomic, strong) NSProgress *downloadProgress;
    @property (nonatomic, copy) NSURL *downloadFileURL;
    @property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; // 下载任务结束回调
    @property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; // 上传进度回调
    @property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; // 下载进度回调
    @property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; // 返回成功结果的Block
    @end
    

    这里遵循了NSURLSessionDataDelegate,NSURLSessionDataDelegate,NSURLSessionDownloadDelegate,所以是将NSURLSession的基本代理功能封装了一下。然后会对这些代理方法的返回数据进行处理,使用completionHandler Block来进行返回。

    首先来看下代理的核心部分,然后再解读progress的封装思路

    代理的核心部分

    #pragma mark - NSURLSessionTaskDelegate
    
    - (void)URLSession:(__unused NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
        __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;
    
            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ // 单利group对象,why?这样是不是就可以对多个返回结果进行处理,这样的话,就不用顺序等待了,哪个先完成就先返回回去。
                // 看参数:如果用户传了group对象,那么选择的是用户的,否则直接使用单利的组对象。 队列是默认是主队列,用户填写的就是用户的。
                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为下载的本地地址
                    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(), ^{ // why use gcd group?
                    if (self.completionHandler) { // 最终的回调结果
                        self.completionHandler(task.response, responseObject, serializationError); // response Header, 返回结果, 解析错误。
                    }
    
                    dispatch_async(dispatch_get_main_queue(), ^{ // 此处任务完成的通知只是为了UIKit中的一些类别中拿到回调。userInfo字段并没有返回给外部,而是给UIKit用的。当然我们可以用这个通知来获取到。
                        [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
                    });
                });
            });
        }
    #pragma clang diagnostic pop
    }
    
    #pragma mark - NSURLSessionDataTaskDelegate
    
    - (void)URLSession:(__unused NSURLSession *)session
              dataTask:(__unused NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
        [self.mutableData appendData:data];
    }
    
    #pragma mark - NSURLSessionDownloadTaskDelegate
    
    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
        NSError *fileManagerError = nil;
        self.downloadFileURL = nil;
    
        if (self.downloadTaskDidFinishDownloading) {
            self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
            if (self.downloadFileURL) {
                [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
    
                if (fileManagerError) {
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
                }
            }
        }
    }
    

    我们可以看到这里核心的方法只有三个,一个是task完成的回调,一个是接受到数据的回调,最后是下载完成的回调。
    普通的task请求会先走第二个方法didReceiveData方法,把返回的数据添加到mutableData中。
    然后再走第一个方法,请求完成的回调。

    第一个方法:

    首先会把返回的数据赋给一个局部变量data,然后将全局的mutableData置空,这样就可以保证下次请求的数据是重新加载。这里区分了下载和普通的数据返回,如果是下载的话,直接下载到指定文件路径中,如果用户指定这个路径的话,userInfo字典里面存的就是下载路径,否则,存的是下载数据。
    然后就是判断有没有错误,错误的话把错误返回,返回值task.response, responseObject此处是空的,然后就是error。最后在主线程中发送通知,为当前task的userInfo,注意此处的userInfo只是用来做通知信息的。而我们平时用的时候不会用通知来获取请求成功的回调,这个通知是为了AFNetworking中的UIKit封装部分服务的。
    然后就是成功的回调,异步请求在单利队列中,这里对队列的生成都加了单利的保护。这里通过responseSerializer 对结果数据进行转化成对应的格式(这可以参考Serialization部分,里面讲解如何转化的),如果是下载的话,responseObject直接赋值成downloadFileURL,也就是下载的话,回调中只会有下载的目标地址。然后就是对userInfo的AFNetworkingTaskDidCompleteSerializedResponseKey(序列化响应结果)、AFNetworkingTaskDidCompleteErrorKey(序列化过程中的错误信息)进行赋值,不得不说AFNetworking对各个部分的情况都返回回去了,做的很详细。
    然后就是调用回调block:completionHandler:
    返回值task.response也就是完整的返回头信息以及返回的状态码。
    responseObject是返回的数据或者是下载的目标地址。
    serializationError注意这个地方的错误是序列化的错误,也就是此处如果对返回数据序列化产生错误,也会照样返回成功回调,只是回调结果会是序列化的错误。
    最后还是一样的发送通知。

    第二个方法:

    简单的接收到返回数据,简单的把数据进行拼接。

    第三个方法:

    下载的方法,此处如果有downloadTaskDidFinishDownloading的实现,那么进行调用转换目标地址,而目标地址是在下面这个方法的destination Block中进行设置的,这个地方转了一下,也就是倒了两个block让用户来设置这个目标地址。如果可以正常获取这个目标地址的话,把location(也就是本地下载路径,其实就是缓存路径,因为这里下载完成后会删掉)的数据转移到目标路径。响应的会有文件操作的错误信息的处理。

    - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
                              progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                           destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                     completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
    {
        AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
        delegate.manager = self;
        delegate.completionHandler = completionHandler;
    
        if (destination) {
            delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
                return destination(location, task.response);
            };
        }
    
        downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
    
        [self setDelegate:delegate forTask:downloadTask];
    
        delegate.downloadProgressBlock = downloadProgressBlock;
    }
    

    网络请求progress的封装思路

    此处使用的技术是KVO,先看代码:

    - (void)setupProgressForTask:(NSURLSessionTask *)task {
        __weak __typeof__(task) weakTask = task;
    
        self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
        self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
        [self.uploadProgress setCancellable:YES];
        [self.uploadProgress setCancellationHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask cancel];
        }];
        [self.uploadProgress setPausable:YES];
        [self.uploadProgress setPausingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask suspend];
        }];
        if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
            [self.uploadProgress setResumingHandler:^{
                __typeof__(weakTask) strongTask = weakTask;
                [strongTask resume];
            }];
        }
    
        [self.downloadProgress setCancellable:YES];
        [self.downloadProgress setCancellationHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask cancel];
        }];
        [self.downloadProgress setPausable:YES];
        [self.downloadProgress setPausingHandler:^{
            __typeof__(weakTask) strongTask = weakTask;
            [strongTask suspend];
        }];
    
        if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
            [self.downloadProgress setResumingHandler:^{
                __typeof__(weakTask) strongTask = weakTask;
                [strongTask resume];
            }];
        }
    
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
    
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
        [task addObserver:self
               forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
                  options:NSKeyValueObservingOptionNew
                  context:NULL];
    
        [self.downloadProgress addObserver:self
                                forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                   options:NSKeyValueObservingOptionNew
                                   context:NULL];
        [self.uploadProgress addObserver:self
                              forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                 options:NSKeyValueObservingOptionNew
                                 context:NULL];
    }
    
    - (void)cleanUpProgressForTask:(NSURLSessionTask *)task {
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))];
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
        [task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))];
        [self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
        [self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
        if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
            if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
                self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
                self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
                self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
                self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
            }
        }
        else if ([object isEqual:self.downloadProgress]) {
            if (self.downloadProgressBlock) {
                self.downloadProgressBlock(object);
            }
        }
        else if ([object isEqual:self.uploadProgress]) {
            if (self.uploadProgressBlock) {
                self.uploadProgressBlock(object);
            }
        }
    }
    

    初始化方法中我们可以看到KVO监听的KeyPath是NSURLSessionTask的四个只读属性:countOfBytesReceived, countOfBytesExpectedToReceive, countOfBytesSent, countOfBytesExpectedToSend, 也就是Task的数据统计的一些信息。 downloadProgress和uploadProgress都对fractionCompleted进行监听,也就是进度完成的百分比。
    这里cleanUp移除所有的KVO,KVO我们平时使用的时候也是需要注意移除的,防止发生原来的还在,又添加监听导致多次监听,那么悲剧了,就是执行监听结果方法的调用越来越多。莫名其妙的bug。
    最后KVO监听到结果的回调中,对downloadProgress,uploadProgress分别进行completedunitCount完成进度的赋值、totalUnitCount所有进度的复制。如果对象不是task而是NSProgress对象,那么直接把这个object返回回去。

    NSURLSession代理方法的转换处理

    首先这里会有很多block的回调:

    - (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block;
    
    - (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
    
    ///--------------------------------------
    /// @name Setting Task Delegate Callbacks
    ///--------------------------------------
    
    - (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block;
    
    - (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block;
    
    - (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
    
    - (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block;
    
    - (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block;
    
    ///-------------------------------------------
    /// @name Setting Data Task Delegate Callbacks
    ///-------------------------------------------
    
    - (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block;
    
    - (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block;
    
    - (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block;
    
    - (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block;
    
    - (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block;
    
    ///-----------------------------------------------
    /// @name Setting Download Task Delegate Callbacks
    ///-----------------------------------------------
    
    - (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable  (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;
    
    - (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block;
    
    - (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block;
    

    也就是支持对所有的NSURLSession代理方法的回调方法直接使用block传给用户使用,这里既支持我们处理原始的回调,AFNetworking在里面继续做响应的处理。

    具体方法的处理,由于太多了,我们就分析一下几个:

    - (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];
    
            [self removeDelegateForTask:task];
        }
    
        if (self.taskDidComplete) {
            self.taskDidComplete(session, task, error);
        }
    }
    

    这个方法会获取到当前task的delegate对象,然后在代理中对这些数据进行处理。这个方法是在网络完成的时候执行的,所以这里应该将这个task移除掉。数据的处理参见3)里面的AFURLSessionManagerTaskDelegate的方法解析。 最后会执行taskDidComplete的block回调。

    - (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); // block 带入目标地址的url,在外面设置
            if (fileURL) {
                delegate.downloadFileURL = fileURL;
                NSError *error = nil;
                [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error];
                if (error) {
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
                }
    
                return;
            }
        }
    
        if (delegate) {
            [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
        }
    }
    

    download的完成方法的回调,我们看到跟AFURLSessionManagerTaskDelegate中的方法功能基本是一样的,也就是如果有AFURLSessionManager类中有downloadTaskDidFinishDownloading的block实现的话,就直接在NSURLSessionDownloadDelegate中处理,如果现在还没有实现block,那么就放在delegete里面处理。注意这个地方AFURLSessionManager和NSURLSessionDownloadDelegate都有这个downloadTaskDidFinishDownloading 的block。

    就这俩吧,其他的代理方法基本上都是转换成block进行回调,有的复杂一点需要对block的值进行处理一下,然后在回调回去。

    关于多任务的处理

    - (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
        NSParameterAssert(task);
    
        AFURLSessionManagerTaskDelegate *delegate = nil;
        [self.lock lock];
        delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
        [self.lock unlock];
    
        return delegate;
    }
    
    - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
                forTask:(NSURLSessionTask *)task
    {
        NSParameterAssert(task);
        NSParameterAssert(delegate);
    
        [self.lock lock];
        self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
        [delegate setupProgressForTask:task];
        [self addNotificationObserverForTask:task];
        [self.lock unlock];
    }
    
    - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                    uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                  downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                 completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
    {
        AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
        delegate.manager = self;
        delegate.completionHandler = completionHandler;
    
        dataTask.taskDescription = self.taskDescriptionForSessionTasks;
        [self setDelegate:delegate forTask:dataTask];
    
        delegate.uploadProgressBlock = uploadProgressBlock;
        delegate.downloadProgressBlock = downloadProgressBlock;
    }
    
    - (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
                            progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
                   completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
    {
        AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
        delegate.manager = self;
        delegate.completionHandler = completionHandler;
    
        uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
    
        [self setDelegate:delegate forTask:uploadTask];
    
        delegate.uploadProgressBlock = uploadProgressBlock;
    }
    
    - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
                              progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
                           destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                     completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
    {
        AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
        delegate.manager = self;
        delegate.completionHandler = completionHandler;
    
        if (destination) {
            delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
                return destination(location, task.response);
            };
        }
    
        downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
    
        [self setDelegate:delegate forTask:downloadTask];
    
        delegate.downloadProgressBlock = downloadProgressBlock;
    }
    
    - (void)removeDelegateForTask:(NSURLSessionTask *)task {
        NSParameterAssert(task);
    
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
        [self.lock lock];
        [delegate cleanUpProgressForTask:task];
        [self removeNotificationObserverForTask:task];
        [self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
        [self.lock unlock];
    }
    

    这里是支持多任务请求的。这里使用字典存的是当前task的代理AFURLSessionManagerTaskDelegate的delegate:
    @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;
    也就是所有的请求会把代理使用task的taskIdentifier标记存起来。设置代理的时候加了保护锁,保证可变字典的安全操作。

    _AFURLSessionTaskSwizzling,使用runtime方法对resume和suspend进行处理task的state

    + (void)load {
        if (NSClassFromString(@"NSURLSessionTask")) {
            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)));
            Class currentClass = [localDataTask class];
            
            while (class_getInstanceMethod(currentClass, @selector(resume))) {
                Class superClass = [currentClass superclass];
                IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
                IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
                if (classResumeIMP != superclassResumeIMP &&
                    originalAFResumeIMP != classResumeIMP) {
                    [self swizzleResumeAndSuspendMethodForClass:currentClass];
                }
                currentClass = [currentClass superclass];
            }
            
            [localDataTask cancel];
            [session finishTasksAndInvalidate];
        }
    }
    
    + (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 { // 通过Swizzle发通知AFNSURLSessionTaskDidResumeNotification
        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];
        }
    }
    

    这里使用runtime技术的swizzle方法对新初始化的一个NSURLSessionDataTask对象进行方法替换。替换resume方法的实现为af_resume,suspend的方法实现为af_suspend。这里面对当前的NSURLSessionTaskState进行操作。

    AFHTTPSessionManager

    这部分是继承自AFURLSessionManager,如果是AFURLSessionManager是网络的核心部分,那么这部分就是方便开发者调用的一个实现部分。
    这里面其实封装的比较简单。看一下属性数据:

    @property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
    
    @property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
    
    @property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
    

    里面一个基本的URL,请求序列化,响应序列化。注意这个地方,请求序列化是在AFHTTPSessionManager中做的处理,而responseSerializer是在AFURLSessionManager中做的处理。

    初始化方法:

    + (instancetype)manager;
    
    - (instancetype)initWithBaseURL:(nullable NSURL *)url;
    
    - (instancetype)initWithBaseURL:(nullable NSURL *)url
               sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
    

    这里支持类方法和实例方法。当然了类方法是封装的实例方法。

    提供的网络请求方法:

    - (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                       parameters:(nullable id)parameters
                          success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                          failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
    
    - (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                                parameters:(nullable id)parameters
                                  progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
                                   success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                   failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    
    - (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
                        parameters:(nullable id)parameters
                           success:(nullable void (^)(NSURLSessionDataTask *task))success
                           failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    
    - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                        parameters:(nullable id)parameters
                           success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                           failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
    
    - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                                 parameters:(nullable id)parameters
                                   progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                    success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                    failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    
    - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                        parameters:(nullable id)parameters
         constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
                           success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                           failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
    
    - (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                                 parameters:(nullable id)parameters
                  constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
                                   progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                    success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                    failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    
    - (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
                       parameters:(nullable id)parameters
                          success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                          failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    
    - (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
                         parameters:(nullable id)parameters
                            success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                            failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    
    - (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
                          parameters:(nullable id)parameters
                             success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                             failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
    

    以上是提供的所有的方法,HTTP协议的基本请求类型GET,POST,PUT,HEAD,DELETE都封装了。

    下面我们看一下GET的实现,因为其他的思路都是一样的,出了POST另外增加了一种body类型的封装。

    - (NSURLSessionDataTask *)GET:(NSString *)URLString
                       parameters:(id)parameters
                         progress:(void (^)(NSProgress * _Nonnull))downloadProgress
                          success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
                          failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
    {
    
        NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
                                                            URLString:URLString
                                                           parameters:parameters
                                                       uploadProgress:nil
                                                     downloadProgress:downloadProgress
                                                              success:success
                                                              failure:failure];
    
        [dataTask resume];
    
        return dataTask;
    }
    

    这里我们可以看到调用的是dataTaskWithHTTPMethod: URLString: parameters: uploadProgress: downloadProgress: success: failure:方法。除了POST新增的方法,其他的都是直接调用的这个方法。

    - (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                           URLString:(NSString *)URLString
                                          parameters:(id)parameters
                                      uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                    downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                             success:(void (^)(NSURLSessionDataTask *, id))success
                                             failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
    {
        NSError *serializationError = nil;
        NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
        if (serializationError) {
            if (failure) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
                dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                    failure(nil, serializationError);
                });
    #pragma clang diagnostic pop
            }
    
            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;
    }
    

    这个方法就是简单的把请求数据进行了序列化,如果错误的话就直接返回nil,不会发送任何请求。成功的话,进行调用AFURLSessionManager中的方法,最后对返回值进行了简单处理。封装的还算简单,没有太多复杂的逻辑。

    最后看一下POST的另一个增加的方法:

    - (NSURLSessionDataTask *)POST:(NSString *)URLString
                        parameters:(id)parameters
         constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
                          progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
                           success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
                           failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
    {
        NSError *serializationError = nil;
        NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
        if (serializationError) {
            if (failure) {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wgnu"
                dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                    failure(nil, serializationError);
                });
    #pragma clang diagnostic pop
            }
    
            return nil;
        }
    
        __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
            if (error) {
                if (failure) {
                    failure(task, error);
                }
            } else {
                if (success) {
                    success(task, responseObject);
                }
            }
        }];
    
        [task resume];
    
        return task;
    }
    

    封装思路跟上面的基本上是一样的,序列化数据的时候会把body的内容封装在请求body里面:
    [self.requestsetHTTPBodyStream:self.bodyStream];
    相应的这里调用的是AFURLSessionManager中的StreamRequest方法。

    如果文中有什么错误,欢迎指正。

    更多问题讨论欢迎加QQ群:200792066

    转载请注明出处:http://semyonxu.com

    相关文章

      网友评论

      本文标题:AFNetworking 3.0 源码解析之NSURLSessi

      本文链接:https://www.haomeiwen.com/subject/mczyyttx.html