iOS源码解析—AFNetworking(URLSession)

概述

上一篇主要分析了基于NSURLConnection的AFURLConnectionOperation,本篇主要分析一下基于NSURLSession方式的相关代码,主要分为核心类AFURLSessionManager及其子类AFHTTPSessionManager。

初始化

AFHTTPSessionManager封装了HTTP请求的几个方法,例如GET、POST等请求,首先通过初始化方法创建AFHTTPSessionManager对象。

初始化过程最终执行到-(instance)initWithBaseURL: sessionConfiguration:方法中,下面是代码注释:

- (instancetype)initWithBaseURL:(NSURL *)url
           sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
    self = [super initWithSessionConfiguration:configuration];
    if (!self) {
        return nil;
    }
    if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
        url = [url URLByAppendingPathComponent:@""];
    }
    self.baseURL = url; //设置baseURL,作为构建url的前缀
    self.requestSerializer = [AFHTTPRequestSerializer serializer];//序列化类
    self.responseSerializer = [AFJSONResponseSerializer serializer];//反序列化类
    return self;
}

首先调用父类对象的initWithSessionConfiguration:方法,初始化一些基本参数。下面是部分代码注释:

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }
    if (!configuration) { //创建默认的NSURLSessionConfiguration对象
        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]; //创建session对象

    self.responseSerializer = [AFJSONResponseSerializer serializer]; //JSON序列化解析类
    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;
    ...
    return self;
}

首先创建一个NSURLSessionConfiguration对象,该对象有三种创建方式,采用默认的方式创建,该对象是属于会话配置项,作用它创建的所有NSURLSession对象。然后创建根据sessionConfiguration对象创建一个session对象,指定delegate和delegate执行的queue,默认是同步队列。然后创建JSON反序列化对象和安全策略对象,网络状态监听对象。mutableTaskDelegatesKeyedByTaskIdentifier字典保存sessionTask和sessionTaskDelegate的关系。同时设置线程锁类控制多线程的数据同步。

接下来在子类AFHTTPSessionManager的方法中设置baseURL,可以作为后续构建Request对象url的前缀,然后设置序列化和反序列化的工具类。

NSURLSessionTask

AFHTTPSessionManager提供了一系列HTTP请求的方法,例如GET、POST、HEAD、DELETE等方法,这些方法的流程相似,都是先根据session对象创建一个task对象,然后调用resume方法启动task。task对象有三种类型,分别是NSURLSessionDataTask、NSURLSessionUploadTask和NSURLSessionDownloadTask,分别如下:

NSURLSessionDataTask
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
    NSError *serializationError = nil;
    //创建request对象,序列化请求报文
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {
        if (failure) {
           //创建失败,抛给上层
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }
        return nil;
    }
    __block NSURLSessionDataTask *dataTask = nil;
    //创建task
    dataTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
        if (error) {
            if (failure) {
                failure(dataTask, error);
            }
        } else {
            if (success) {
                success(dataTask, responseObject);
            }
        }
    }];
    return dataTask;
}

首先构建http请求的报文,包括请求头、请求体等信息,创建request对象,然后调用dataTaskWithRequest: completionHandler:方法创建NSURLSessionDataTask对象,方法如下:

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    __block NSURLSessionDataTask *dataTask = nil;
    dispatch_sync(url_session_manager_creation_queue(), ^{
        dataTask = [self.session dataTaskWithRequest:request]; //创建task对象
    });
    [self addDelegateForDataTask:dataTask completionHandler:completionHandler]; //创建delegate,绑定completionHandler回调,关联delegate
    return dataTask;
}

首先在串行队列中创建一个task对象,使用dispatch_sync保证线程阻塞直到创建完成,为什么不直接在主线程做不太清楚,然后调用addDelegateForDataTask方法为task对象添加delegate对象,并绑定completionHandler回调block,用于http请求完成后执行。addDelegateForDataTask方法注释如下:

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; //创建delegate对象
    delegate.manager = self;
    delegate.completionHandler = completionHandler; //绑定completionHandler
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask]; //关联task个delegate
}

首先创建一个delegate对象,绑定completionHandler,然后调用setDelegate:forTask:方法关联task和delegate,代码注释如下,

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; //存入字典
    [delegate setupProgressForTask:task]; //delegate监听task的进度
    [self addNotificationObserverForTask:task]; //添加通知
    [self.lock unlock];
}

通过mutableTaskDelegatesKeyedByTaskIdentifier字典维护关系,key是task的taskIdentifier,value是delegate,通过setupProgressForTask方法使delegate监听task的进度。

NSURLSessionUploadTask

当需要上传一个数据到服务端,需要创建NSURLSessionUploadTask,创建方式有三种:

uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
uploadTask = [self.session uploadTaskWithStreamedRequest:request];

支持从url地址、内存、流中读取数据,创建task的流程和NSURLSessionDataTask类似,创建完成后需要为其添加一个delegate对象并绑定,如下:

- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
                                         fromData:(NSData *)bodyData
                                         progress:(NSProgress * __autoreleasing *)progress
                                completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    __block NSURLSessionUploadTask *uploadTask = nil;
    dispatch_sync(url_session_manager_creation_queue(), ^{
        uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; //创建一个uploadTask对象
    });
    [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; //添加delegate
    return uploadTask;
}

addDelegateForUploadTask:progress:completionHandler:方法为uploadTask设置delegate,代码注释如下:

- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
                        progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
               completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; //创建delegate对象
    delegate.manager = self;
    delegate.completionHandler = completionHandler;
    uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:uploadTask]; //绑定delegate和uploadTask对象
    delegate.uploadProgressBlock = uploadProgressBlock; //设置上传进度回调blcok
}

首先创建delegate对象,然后绑定delegate和uploadTask对象,最后设置uploadProgressBlock,用于通知外界上传数据的进度。

NSURLSessionDownloadTask

当需要下载数据并写入指定地址时,需要创建NSURLSessionDownloadTask对象:

- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
                                             progress:(NSProgress * __autoreleasing *)progress
                                          destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
                                    completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
    __block NSURLSessionDownloadTask *downloadTask = nil;
    dispatch_sync(url_session_manager_creation_queue(), ^{
        downloadTask = [self.session downloadTaskWithRequest:request]; //创建一个NSURLSessionDownloadTask
    });
    [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; //创建delegate,绑定
    return downloadTask;
}

其中destination是一个block,返回一个url,表示需要写入数据的地址。addDelegateForDownloadTask方法和upload的类似,代码注释如下:

- (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对象
    delegate.manager = self;
    delegate.completionHandler = completionHandler; //绑定completionHandler
    if (destination) {
        //设置block,返回写入下载数据的地址
        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和downloadTask对象
    delegate.downloadProgressBlock = downloadProgressBlock; //设置下载进度回调blcok
}

AFURLSessionManagerTaskDelegate

AFURLSessionManagerTaskDelegate是代理对象,主要作用有两个,一是监听task的进度,并回抛给上层,二是处理task的回调方法,delegate和task是一一对应关系。下面是类的定义:

@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; //上传进度block
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; //下载进度blcok
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; //请求完成block
@end

mutableData负责接收和拼装server响应的数据,uploadProgress和downloadProgress负责管理发送和接收数据的进度,uploadProgressBlock和downloadProgressBlock负责将进度回抛给上层。completionHandler是请求完成时调用的blcok,负责将响应数据回抛给上层。downloadFileURL是存储下载数据的本地地址,通过执行downloadTaskDidFinishDownloading回调得到。

  1. 监听进度

    调用setupProgressForTask:方法关联task的进度属性,注释如下:

    - (void)setupProgressForTask:(NSURLSessionTask *)task {
        __weak __typeof__(task) weakTask = task;
     //1.task需要传输的所有数据大小
        self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
        self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
         //2.关联progress和task的状态  
         [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];
            }];
        }
     //3.监听task的数据传输状态
        [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];
     //4.监听progress的fractionCompleted属性值
        [self.downloadProgress addObserver:self                        forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                   options:NSKeyValueObservingOptionNew
                                   context:NULL];
        [self.uploadProgress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
                                 options:NSKeyValueObservingOptionNew
                                 context:NULL];
    }
    

    该方法首先获取当前task需要发送或者接受的数据总大小。然后关联progress和task的状态,当progress暂停时,相应的task需要suspend,当progress取消时,task会cancel,当progress启动时,task会resume。然后通过kvo的方式监听task的进度属性,countOfBytesReceived和countOfBytesSent表示当前的接受和发送的数据量,countOfBytesExpectedToReceive和countOfBytesExpectedToSend表示需要接受和发送的数据总量。同时监听progress的fractionCompleted属性,fractionCompleted属性表示任务的整体进度,值从0到1,kvo的响应方法注释如下:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
        if ([object isKindOfClass:[NSURLSessionTask class]]) {
            if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
                self.downloadProgress.completedUnitCount = [change[@"new"] longLongValue]; //当前接受的数据量
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
                self.downloadProgress.totalUnitCount = [change[@"new"] longLongValue]; //需要接受的数据总量
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
                self.uploadProgress.completedUnitCount = [change[@"new"] longLongValue]; //当前发送的数据量
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
                self.uploadProgress.totalUnitCount = [change[@"new"] 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); //进度回抛给上层
            }
        }
    }
    

    该方法根据监听到的属性,实时更新progress的进度属性,然后将属性回抛给上层。delegate调用cleanUpProgressForTask注销kvo。

  2. 处理URLSessionTask回调

    在初始化的方法时,通过[NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]将AFURLSessionManager对象设置为session的delegate,通过URLSession创建出来的每个task,在执行的过程中,即网络请求的过程中,都会调用AFURLSessionManager对象来处理一系列代理方法。然后AFURLSessionManager对象进一步调用AFURLSessionManagerTaskDelegate对象来处理,具体实现在下文分析。

AFURLSessionManager

AFURLSessionManager是处理网络请求回调的核心类,主要负责处理NSURLSession以及NSURLSessionTask的各种回调逻辑。下面分析一下主要的delegate方法:

NSURLSessionDelegate
  1. -(void)URLSession: didReceiveChallenge: completionHandler方法

    发HTTPS请求时,在SSL握手过程中,服务端将证书下发客户端,客户端需要校验服务端证书,在AF代码中,核心代码注释如下:

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { //如果需要验证trust对象
     if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { //用securityPolicy对象验证trust对象
          credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; //验证通过,根据trust对象生成证书凭证
          if (credential) {
         disposition = NSURLSessionAuthChallengeUseCredential; //使用凭证
         } else {
             disposition = NSURLSessionAuthChallengePerformDefaultHandling; //默认方式
         }
     } else {
         disposition = NSURLSessionAuthChallengeRejectProtectionSpace; //验证失败,终止
     }
    } else {
     disposition = NSURLSessionAuthChallengePerformDefaultHandling; //用默认方式
    }
    ...
    if (completionHandler) {
     completionHandler(disposition, credential);
    }
    

    该方法首先判断是否需要验证trust对象,trust是一个SecTrustRef类型的对象,用于对服务器端传来的X.509证书评估,然后用securityPolicy进行验证。如果验证通过,则用trust对象生成一个证书凭证,并采用NSURLSessionAuthChallengeUseCredential的方式。如果不能生成凭证,则用默认方式。最终将方式和凭证通过completionHandler的方式回调给系统,进行后续的校验和连接。

NSURLSessionTaskDelegate
  1. -(void)NSURLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:方法

    该方法在服务端重定向一个url的时候会触发,处理代码注释如下:

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    willPerformHTTPRedirection:(NSHTTPURLResponse *)response
            newRequest:(NSURLRequest *)request
     completionHandler:(void (^)(NSURLRequest *))completionHandler
    {
        NSURLRequest *redirectRequest = request; //用默认重定向的request
    if (self.taskWillPerformHTTPRedirection) { //如果有自定义block,则用block返回的request对象
     redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
    }
     if (completionHandler) { //回调给系统
         completionHandler(redirectRequest);
     }
    }
    
  2. -(void)URLSession:task:didReceiveChallenge:completionHandler:方法

    该方法的逻辑和URLSessionDelegate的方法相同,但是task级别的方法,同时在两个方法都实现的情况下,会优先进入上文方法进行校验,不进入该方法校验。

  3. -(void)URLSession:task:needNewBodyStream方法

    如果通过stream的方式上传数据至服务器,会调用[self.session uploadTaskWithStreamedRequest:request]方法创建NSURLSessionUploadTask,在执行task发送请求时,会触发该方法,代码注释如下:

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
     needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler
    {
        NSInputStream *inputStream = nil;
        if (self.taskNeedNewBodyStream) { //自定义block处理,返回新的stream
            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);
        }
    }
    
  4. -(void)URLSession:task:didSendBodyData: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) { //用contentLength替换totalBytesExpectedToSend
                totalUnitCount = (int64_t) [contentLength longLongValue];
            }
        }
     //回调给上层,包括当前一次发送的数据、发送数据的数据总量、需要发送的数据量
        if (self.taskDidSendBodyData) {
            self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
        }
    }
    

    bytesSent表示当前一次发送的数据量,totalBytesSent表示已经发送的数据总量,totalUnitCount表示本次请求需要发送的数据总量。

  5. -(void)URLSession:task:didCompleteWithError:

    当前task完成时,会触发该回调方法,NSURLSessionDataTask、NSURLSessionDownloadTask、NSURLSessionUploadTask结束时都会调用该方法,代码注释如下:

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; 
        if (delegate) { //调用delegate处理回调
            [delegate URLSession:session task:task didCompleteWithError:error];
            [self removeDelegateForTask:task]; //删除task对应的delegate
        }
        if (self.taskDidComplete) { //如果有回调block,执行block
            self.taskDidComplete(session, task, error);
        }
    }
    

    首先通过task的taskIdentifier找到对应的delegate,然后调用delegate的方法处理,最后再删除delegate,如果有回调block,执行block。下面是delegate对象方法的注释:

    - (void)URLSession:(__unused NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
     ...
        //data是响应报文数据
        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 (error) { //如果请求失败
            userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
    
            dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
                if (self.completionHandler) { //在指定队列或者主队列中将error回调给上层
                    self.completionHandler(task.response, responseObject, error);
                }
                ...
                });
            });
        } else {
            dispatch_async(url_session_manager_processing_queue(), ^{
                NSError *serializationError = nil;
                 //反序列化响应报文数据
                responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
    
                if (self.downloadFileURL) {
                     //如果是downloadTask,responseObject是downloadFileURL
                    responseObject = self.downloadFileURL;
                }
                 ...
                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); //在指定队列或者主队列中将反序列后的对象responseObject回调给上层
                    }
                });
                 ...
            });
        }
    #pragma clang diagnostic pop
    }
    

    该方法主要是获取到服务端响应报文数据后,用responseSerializer反序列化数据,并回调给上层,当创建一个NSURLSessionDataTask类型的task并执行时,会得到报文数据mutableData。如果创建NSURLSessionDownloadTask执行,直接下载数据到本地,mutableData为nil。反序列化的过程在异步队列中执行,提高了性能。

NSURLSessionDataDelegate

使用NSURLSessionDataTask发HTTP请求时,在请求的过程中,会触发以下代理方法:

  1. -(void)URLSession:dataTask:didReceiveResponse:completionHandler:方法

    当开始接收到服务器的响应时,该方法被调用,代码注释如下:

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveResponse:(NSURLResponse *)response
     completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
    {
       //默认处理方式
        NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
        if (self.dataTaskDidReceiveResponse) { //如果实现block,通过block修改处理方式
            disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
        }
        if (completionHandler) { //回调给系统
            completionHandler(disposition);
        }
    }
    

    当接收到服务器响应时,说明网络请求已经连接,默认是NSURLSessionResponseAllow表示允许task继续执行,进行通信和数据传输,如果外部设置了dataTaskDidReceiveResponse的block,则可以修改通过block修改处理方式。其他的处理方式有:

    NSURLSessionResponseCancel //取消task

    NSURLSessionResponseAllow //允许task继续执行

    NSURLSessionResponseBecomeDownload //dataTask变成downloadTask

    NSURLSessionResponseBecomeStream //变成NSURLSessionStreamTask

  2. -(void)URLSession:dataTask:didBecomeDownloadTask:

    上一个方法的disposition设置为NSURLSessionResponseBecomeDownload时,dataTask变成了downloadTask,会触发该方法,需要对将之前delegate关联到新的task。代码注释如下:

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
    didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
        if (delegate) { //重新关联delegate和task
            [self removeDelegateForTask:dataTask];
            [self setDelegate:delegate forTask:downloadTask];
        }
        if (self.dataTaskDidBecomeDownloadTask) {
            self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
        }
    }
    
  3. -(void)URLSession:dataTask:didReceiveData:

    当接收服务端响应后并开始传输数据时,该方法一次或者多次被出发,返回服务端响应的报文数据,在该方法中调用delegate拼接报文数据。

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
    
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
        //delegate处理data数据
        [delegate URLSession:session dataTask:dataTask didReceiveData:data];
    
        if (self.dataTaskDidReceiveData) {
            self.dataTaskDidReceiveData(session, dataTask, data);
        }
    }
    

    AFURLSessionManagerTaskDelegate对象负责处理响应报文数据data:

    - (void)URLSession:(__unused NSURLSession *)session
              dataTask:(__unused NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
        [self.mutableData appendData:data]; //拼接到mutableData中
    }
    

    delegate将data拼接到mutableData中。

  4. -(void)URLSession:dataTask:willCacheResponse:completionHandler:方法

    当构建一个request对象时,默认的缓存属性策略cachePolicy是NSURLRequestUseProtocolCachePolicy,即根据服务端响应报文的header中的缓存策略决定是否缓存数据,当需要缓存数据时,会触发该方法,将响应报文数据缓存到本地,实现该方法可以修改需要缓存的数据。

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
     willCacheResponse:(NSCachedURLResponse *)proposedResponse
     completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
    {
        NSCachedURLResponse *cachedResponse = proposedResponse;
    
        if (self.dataTaskWillCacheResponse) { //如果实现block,通过block修改缓存数据
            cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
        }
        if (completionHandler) { //回调给系统,存储缓存数据
            completionHandler(cachedResponse);
        }
    }
    
NSURLSessionDownloadDelegate
  1. URLSession:downloadTask:didFinishDownloadingToURL:

    执行NSURLSessionDownloadTask下载数据到本地,当下载完成时,会触发该方法,代码注释如下:

    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
        if (self.downloadTaskDidFinishDownloading) { //如果实现block,执行下面逻辑
            NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); //获取需要写入数据的地址fileURL
            if (fileURL) {
                delegate.downloadFileURL = fileURL;
                NSError *error = nil;
                [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]; //将写入location地址的数据移至fileURL
                if (error) {
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
                }
                return;
            }
        }
        if (delegate) { //通过delegate处理下载完成的逻辑
            [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
        }
    }
    

    如果实现了downloadTaskDidFinishDownloading的block,执行block提供写入下载数据的地址fileURL,因为之前系统默认将下载数据写入location地址,会将location的数据移至fileURL。如果没有实现block,默认调用delegate的方法处理逻辑,代码注释如下:

    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
        ...
        if (self.downloadTaskDidFinishDownloading) {
            self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); //获取下载地址downloadFileURL
            if (self.downloadFileURL) {//将location中的数据移至downloadFileURL
                [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
            ...  
            }
        }
    }
    

    该方法执行downloadTaskDidFinishDownloading获取需要写入下载数据的地址fileURL,在之前的addDelegateForDownloadTask方法中设置了downloadTaskDidFinishDownloading。然后将默认location地址中的下载数据移动至fileURL。

  2. URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:

    执行NSURLSessionDownloadTask下载数据的过程中,会触发该方法通知下载的进度,代码注释如下:

    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
          didWriteData:(int64_t)bytesWritten
     totalBytesWritten:(int64_t)totalBytesWritten
    totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
    {
        if (self.downloadTaskDidWriteData) { //将进度数据回调给外界
            self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
        }
    }
    

    bytesWritten是本次接收到的数据,totalBytesWritten是已经接受到数据字节数,totalBytesExpectedToWrite是需要下载的总数据量。

总结

AFURLSessionManager实现了NSURLSession、NSURLSessionTask及其子类的各种代理回调方法,同时提供了各种自定义block,增强了代码逻辑的灵活性,同时可以监听数据传输的进度,通知外界。该模块对于网络请求的逻辑封装的比较好,相关代码值得学习。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容