iOS源碼解析—AFNetworking(URLSession)

概述

上一篇主要分析了基于NSURLConnection的AFURLConnectionOperation细移,本篇主要分析一下基于NSURLSession方式的相關(guān)代碼,主要分為核心類AFURLSessionManager及其子類AFHTTPSessionManager爽茴。

初始化

AFHTTPSessionManager封裝了HTTP請求的幾個方法寒矿,例如GET蹋半、POST等請求鹦聪,首先通過初始化方法創(chuàng)建AFHTTPSessionManager對象账阻。

初始化過程最終執(zhí)行到-(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; //設(shè)置baseURL泽本,作為構(gòu)建url的前綴
    self.requestSerializer = [AFHTTPRequestSerializer serializer];//序列化類
    self.responseSerializer = [AFJSONResponseSerializer serializer];//反序列化類
    return self;
}

首先調(diào)用父類對象的initWithSessionConfiguration:方法淘太,初始化一些基本參數(shù)。下面是部分代碼注釋:

- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
    self = [super init];
    if (!self) {
        return nil;
    }
    if (!configuration) { //創(chuàng)建默認(rèn)的NSURLSessionConfiguration對象
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }
    self.sessionConfiguration = configuration;
    self.operationQueue = [[NSOperationQueue alloc] init];
    self.operationQueue.maxConcurrentOperationCount = 1; //最大并發(fā)數(shù)為1规丽,同步隊列

    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; //創(chuàng)建session對象

    self.responseSerializer = [AFJSONResponseSerializer serializer]; //JSON序列化解析類
    self.securityPolicy = [AFSecurityPolicy defaultPolicy]; //默認(rèn)安全策略類

#if !TARGET_OS_WATCH
    self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; //網(wǎng)絡(luò)狀態(tài)監(jiān)聽類
#endif
    self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
    self.lock = [[NSLock alloc] init]; //線程鎖
    self.lock.name = AFURLSessionManagerLockName;
    ...
    return self;
}

首先創(chuàng)建一個NSURLSessionConfiguration對象蒲牧,該對象有三種創(chuàng)建方式,采用默認(rèn)的方式創(chuàng)建赌莺,該對象是屬于會話配置項(xiàng)造成,作用它創(chuàng)建的所有NSURLSession對象。然后創(chuàng)建根據(jù)sessionConfiguration對象創(chuàng)建一個session對象雄嚣,指定delegate和delegate執(zhí)行的queue晒屎,默認(rèn)是同步隊列。然后創(chuàng)建JSON反序列化對象和安全策略對象缓升,網(wǎng)絡(luò)狀態(tài)監(jiān)聽對象鼓鲁。mutableTaskDelegatesKeyedByTaskIdentifier字典保存sessionTask和sessionTaskDelegate的關(guān)系。同時設(shè)置線程鎖類控制多線程的數(shù)據(jù)同步港谊。

接下來在子類AFHTTPSessionManager的方法中設(shè)置baseURL骇吭,可以作為后續(xù)構(gòu)建Request對象url的前綴,然后設(shè)置序列化和反序列化的工具類歧寺。

NSURLSessionTask

AFHTTPSessionManager提供了一系列HTTP請求的方法燥狰,例如GET、POST斜筐、HEAD龙致、DELETE等方法,這些方法的流程相似顷链,都是先根據(jù)session對象創(chuàng)建一個task對象目代,然后調(diào)用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;
    //創(chuàng)建request對象煞抬,序列化請求報文
    NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
    if (serializationError) {
        if (failure) {
           //創(chuàng)建失敗霜大,拋給上層
            dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
                failure(nil, serializationError);
            });
        }
        return nil;
    }
    __block NSURLSessionDataTask *dataTask = nil;
    //創(chuàng)建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;
}

首先構(gòu)建http請求的報文,包括請求頭革答、請求體等信息战坤,創(chuàng)建request對象遮婶,然后調(diào)用dataTaskWithRequest: completionHandler:方法創(chuàng)建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]; //創(chuàng)建task對象
    });
    [self addDelegateForDataTask:dataTask completionHandler:completionHandler]; //創(chuàng)建delegate湖笨,綁定completionHandler回調(diào)旗扑,關(guān)聯(lián)delegate
    return dataTask;
}

首先在串行隊列中創(chuàng)建一個task對象,使用dispatch_sync保證線程阻塞直到創(chuàng)建完成慈省,為什么不直接在主線程做不太清楚臀防,然后調(diào)用addDelegateForDataTask方法為task對象添加delegate對象,并綁定completionHandler回調(diào)block边败,用于http請求完成后執(zhí)行袱衷。addDelegateForDataTask方法注釋如下:

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
             completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; //創(chuàng)建delegate對象
    delegate.manager = self;
    delegate.completionHandler = completionHandler; //綁定completionHandler
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask]; //關(guān)聯(lián)task個delegate
}

首先創(chuàng)建一個delegate對象,綁定completionHandler笑窜,然后調(diào)用setDelegate:forTask:方法關(guān)聯(lián)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監(jiān)聽task的進(jìn)度
    [self addNotificationObserverForTask:task]; //添加通知
    [self.lock unlock];
}

通過mutableTaskDelegatesKeyedByTaskIdentifier字典維護(hù)關(guān)系排截,key是task的taskIdentifier嫌蚤,value是delegate,通過setupProgressForTask方法使delegate監(jiān)聽task的進(jìn)度断傲。

NSURLSessionUploadTask

當(dāng)需要上傳一個數(shù)據(jù)到服務(wù)端脱吱,需要創(chuàng)建NSURLSessionUploadTask,創(chuàng)建方式有三種:

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

支持從url地址认罩、內(nèi)存箱蝠、流中讀取數(shù)據(jù),創(chuàng)建task的流程和NSURLSessionDataTask類似垦垂,創(chuàng)建完成后需要為其添加一個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]; //創(chuàng)建一個uploadTask對象
    });
    [self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; //添加delegate
    return uploadTask;
}

addDelegateForUploadTask:progress:completionHandler:方法為uploadTask設(shè)置delegate,代碼注釋如下:

- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
                        progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
               completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; //創(chuàng)建delegate對象
    delegate.manager = self;
    delegate.completionHandler = completionHandler;
    uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:uploadTask]; //綁定delegate和uploadTask對象
    delegate.uploadProgressBlock = uploadProgressBlock; //設(shè)置上傳進(jìn)度回調(diào)blcok
}

首先創(chuàng)建delegate對象劫拗,然后綁定delegate和uploadTask對象间校,最后設(shè)置uploadProgressBlock,用于通知外界上傳數(shù)據(jù)的進(jìn)度杨幼。

NSURLSessionDownloadTask

當(dāng)需要下載數(shù)據(jù)并寫入指定地址時撇簿,需要創(chuàng)建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]; //創(chuàng)建一個NSURLSessionDownloadTask
    });
    [self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; //創(chuàng)建delegate,綁定
    return downloadTask;
}

其中destination是一個block差购,返回一個url,表示需要寫入數(shù)據(jù)的地址汉嗽。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]; //創(chuàng)建delegate對象
    delegate.manager = self;
    delegate.completionHandler = completionHandler; //綁定completionHandler
    if (destination) {
        //設(shè)置block,返回寫入下載數(shù)據(jù)的地址
        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; //設(shè)置下載進(jìn)度回調(diào)blcok
}

AFURLSessionManagerTaskDelegate

AFURLSessionManagerTaskDelegate是代理對象饼暑,主要作用有兩個稳析,一是監(jiān)聽task的進(jìn)度洗做,并回拋給上層,二是處理task的回調(diào)方法彰居,delegate和task是一一對應(yīng)關(guān)系诚纸。下面是類的定義:

@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
@property (nonatomic, weak) AFURLSessionManager *manager;
@property (nonatomic, strong) NSMutableData *mutableData; //響應(yīng)數(shù)據(jù)
@property (nonatomic, strong) NSProgress *uploadProgress; //上傳進(jìn)度
@property (nonatomic, strong) NSProgress *downloadProgress; //下載進(jìn)度
@property (nonatomic, copy) NSURL *downloadFileURL; //下載地址
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; //下載完成回調(diào)
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; //上傳進(jìn)度block
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; //下載進(jìn)度blcok
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; //請求完成block
@end

mutableData負(fù)責(zé)接收和拼裝server響應(yīng)的數(shù)據(jù),uploadProgress和downloadProgress負(fù)責(zé)管理發(fā)送和接收數(shù)據(jù)的進(jìn)度陈惰,uploadProgressBlock和downloadProgressBlock負(fù)責(zé)將進(jìn)度回拋給上層畦徘。completionHandler是請求完成時調(diào)用的blcok,負(fù)責(zé)將響應(yīng)數(shù)據(jù)回拋給上層抬闯。downloadFileURL是存儲下載數(shù)據(jù)的本地地址井辆,通過執(zhí)行downloadTaskDidFinishDownloading回調(diào)得到。

  1. 監(jiān)聽進(jìn)度

    調(diào)用setupProgressForTask:方法關(guān)聯(lián)task的進(jìn)度屬性溶握,注釋如下:

    - (void)setupProgressForTask:(NSURLSessionTask *)task {
        __weak __typeof__(task) weakTask = task;
     //1.task需要傳輸?shù)乃袛?shù)據(jù)大小
        self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
        self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
         //2.關(guān)聯(lián)progress和task的狀態(tài)  
         [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.監(jiān)聽task的數(shù)據(jù)傳輸狀態(tài)
        [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.監(jiān)聽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];
    }
    

    該方法首先獲取當(dāng)前task需要發(fā)送或者接受的數(shù)據(jù)總大小杯缺。然后關(guān)聯(lián)progress和task的狀態(tài),當(dāng)progress暫停時睡榆,相應(yīng)的task需要suspend萍肆,當(dāng)progress取消時,task會cancel胀屿,當(dāng)progress啟動時匾鸥,task會resume。然后通過kvo的方式監(jiān)聽task的進(jìn)度屬性碉纳,countOfBytesReceived和countOfBytesSent表示當(dāng)前的接受和發(fā)送的數(shù)據(jù)量勿负,countOfBytesExpectedToReceive和countOfBytesExpectedToSend表示需要接受和發(fā)送的數(shù)據(jù)總量。同時監(jiān)聽progress的fractionCompleted屬性劳曹,fractionCompleted屬性表示任務(wù)的整體進(jìn)度奴愉,值從0到1,kvo的響應(yīng)方法注釋如下:

    - (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]; //當(dāng)前接受的數(shù)據(jù)量
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
                self.downloadProgress.totalUnitCount = [change[@"new"] longLongValue]; //需要接受的數(shù)據(jù)總量
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
                self.uploadProgress.completedUnitCount = [change[@"new"] longLongValue]; //當(dāng)前發(fā)送的數(shù)據(jù)量
            } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
                self.uploadProgress.totalUnitCount = [change[@"new"] longLongValue]; //需要發(fā)送的數(shù)據(jù)總量
            }
        }
        else if ([object isEqual:self.downloadProgress]) {
            if (self.downloadProgressBlock) {
                self.downloadProgressBlock(object); //進(jìn)度回拋給上層
            }
        }
        else if ([object isEqual:self.uploadProgress]) {
            if (self.uploadProgressBlock) {
                self.uploadProgressBlock(object); //進(jìn)度回拋給上層
            }
        }
    }
    

    該方法根據(jù)監(jiān)聽到的屬性铁孵,實(shí)時更新progress的進(jìn)度屬性锭硼,然后將屬性回拋給上層。delegate調(diào)用cleanUpProgressForTask注銷kvo蜕劝。

  2. 處理URLSessionTask回調(diào)

    在初始化的方法時檀头,通過[NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]將AFURLSessionManager對象設(shè)置為session的delegate,通過URLSession創(chuàng)建出來的每個task岖沛,在執(zhí)行的過程中暑始,即網(wǎng)絡(luò)請求的過程中,都會調(diào)用AFURLSessionManager對象來處理一系列代理方法婴削。然后AFURLSessionManager對象進(jìn)一步調(diào)用AFURLSessionManagerTaskDelegate對象來處理廊镜,具體實(shí)現(xiàn)在下文分析。

AFURLSessionManager

AFURLSessionManager是處理網(wǎng)絡(luò)請求回調(diào)的核心類唉俗,主要負(fù)責(zé)處理NSURLSession以及NSURLSessionTask的各種回調(diào)邏輯嗤朴。下面分析一下主要的delegate方法:

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

    發(fā)HTTPS請求時配椭,在SSL握手過程中,服務(wù)端將證書下發(fā)客戶端雹姊,客戶端需要校驗(yàn)服務(wù)端證書股缸,在AF代碼中,核心代碼注釋如下:

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { //如果需要驗(yàn)證trust對象
     if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { //用securityPolicy對象驗(yàn)證trust對象
          credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; //驗(yàn)證通過吱雏,根據(jù)trust對象生成證書憑證
          if (credential) {
         disposition = NSURLSessionAuthChallengeUseCredential; //使用憑證
         } else {
             disposition = NSURLSessionAuthChallengePerformDefaultHandling; //默認(rèn)方式
         }
     } else {
         disposition = NSURLSessionAuthChallengeRejectProtectionSpace; //驗(yàn)證失敗敦姻,終止
     }
    } else {
     disposition = NSURLSessionAuthChallengePerformDefaultHandling; //用默認(rèn)方式
    }
    ...
    if (completionHandler) {
     completionHandler(disposition, credential);
    }
    

    該方法首先判斷是否需要驗(yàn)證trust對象,trust是一個SecTrustRef類型的對象坎背,用于對服務(wù)器端傳來的X.509證書評估替劈,然后用securityPolicy進(jìn)行驗(yàn)證。如果驗(yàn)證通過得滤,則用trust對象生成一個證書憑證陨献,并采用NSURLSessionAuthChallengeUseCredential的方式。如果不能生成憑證懂更,則用默認(rèn)方式眨业。最終將方式和憑證通過completionHandler的方式回調(diào)給系統(tǒng),進(jìn)行后續(xù)的校驗(yàn)和連接沮协。

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

    該方法在服務(wù)端重定向一個url的時候會觸發(fā)龄捡,處理代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    willPerformHTTPRedirection:(NSHTTPURLResponse *)response
            newRequest:(NSURLRequest *)request
     completionHandler:(void (^)(NSURLRequest *))completionHandler
    {
        NSURLRequest *redirectRequest = request; //用默認(rèn)重定向的request
    if (self.taskWillPerformHTTPRedirection) { //如果有自定義block,則用block返回的request對象
     redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
    }
     if (completionHandler) { //回調(diào)給系統(tǒng)
         completionHandler(redirectRequest);
     }
    }
    
  2. -(void)URLSession:task:didReceiveChallenge:completionHandler:方法

    該方法的邏輯和URLSessionDelegate的方法相同慷暂,但是task級別的方法聘殖,同時在兩個方法都實(shí)現(xiàn)的情況下,會優(yōu)先進(jìn)入上文方法進(jìn)行校驗(yàn)行瑞,不進(jìn)入該方法校驗(yàn)奸腺。

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

    如果通過stream的方式上傳數(shù)據(jù)至服務(wù)器,會調(diào)用[self.session uploadTaskWithStreamedRequest:request]方法創(chuàng)建NSURLSessionUploadTask血久,在執(zhí)行task發(fā)送請求時突照,會觸發(fā)該方法,代碼注釋如下:

    - (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];//默認(rèn)的字節(jié)流
        }
        if (completionHandler) { //回調(diào)給系統(tǒng)
            completionHandler(inputStream);
        }
    }
    
  4. -(void)URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:

    當(dāng)發(fā)送請求報文時讹蘑,觸發(fā)該方法,返回發(fā)送的數(shù)據(jù)量等信息筑舅。

    - (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];
            }
        }
     //回調(diào)給上層座慰,包括當(dāng)前一次發(fā)送的數(shù)據(jù)、發(fā)送數(shù)據(jù)的數(shù)據(jù)總量豁翎、需要發(fā)送的數(shù)據(jù)量
        if (self.taskDidSendBodyData) {
            self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
        }
    }
    

    bytesSent表示當(dāng)前一次發(fā)送的數(shù)據(jù)量角骤,totalBytesSent表示已經(jīng)發(fā)送的數(shù)據(jù)總量,totalUnitCount表示本次請求需要發(fā)送的數(shù)據(jù)總量心剥。

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

    當(dāng)前task完成時邦尊,會觸發(fā)該回調(diào)方法,NSURLSessionDataTask优烧、NSURLSessionDownloadTask蝉揍、NSURLSessionUploadTask結(jié)束時都會調(diào)用該方法,代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; 
        if (delegate) { //調(diào)用delegate處理回調(diào)
            [delegate URLSession:session task:task didCompleteWithError:error];
            [self removeDelegateForTask:task]; //刪除task對應(yīng)的delegate
        }
        if (self.taskDidComplete) { //如果有回調(diào)block畦娄,執(zhí)行block
            self.taskDidComplete(session, task, error);
        }
    }
    

    首先通過task的taskIdentifier找到對應(yīng)的delegate又沾,然后調(diào)用delegate的方法處理,最后再刪除delegate熙卡,如果有回調(diào)block杖刷,執(zhí)行block。下面是delegate對象方法的注釋:

    - (void)URLSession:(__unused NSURLSession *)session
                  task:(NSURLSessionTask *)task
    didCompleteWithError:(NSError *)error
    {
     ...
        //data是響應(yīng)報文數(shù)據(jù)
        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回調(diào)給上層
                    self.completionHandler(task.response, responseObject, error);
                }
                ...
                });
            });
        } else {
            dispatch_async(url_session_manager_processing_queue(), ^{
                NSError *serializationError = nil;
                 //反序列化響應(yīng)報文數(shù)據(jù)
                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回調(diào)給上層
                    }
                });
                 ...
            });
        }
    #pragma clang diagnostic pop
    }
    

    該方法主要是獲取到服務(wù)端響應(yīng)報文數(shù)據(jù)后滑燃,用responseSerializer反序列化數(shù)據(jù),并回調(diào)給上層颓鲜,當(dāng)創(chuàng)建一個NSURLSessionDataTask類型的task并執(zhí)行時表窘,會得到報文數(shù)據(jù)mutableData。如果創(chuàng)建NSURLSessionDownloadTask執(zhí)行甜滨,直接下載數(shù)據(jù)到本地乐严,mutableData為nil。反序列化的過程在異步隊列中執(zhí)行衣摩,提高了性能昂验。

NSURLSessionDataDelegate

使用NSURLSessionDataTask發(fā)HTTP請求時,在請求的過程中艾扮,會觸發(fā)以下代理方法:

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

    當(dāng)開始接收到服務(wù)器的響應(yīng)時既琴,該方法被調(diào)用,代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveResponse:(NSURLResponse *)response
     completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
    {
       //默認(rèn)處理方式
        NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
        if (self.dataTaskDidReceiveResponse) { //如果實(shí)現(xiàn)block栏渺,通過block修改處理方式
            disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
        }
        if (completionHandler) { //回調(diào)給系統(tǒng)
            completionHandler(disposition);
        }
    }
    

    當(dāng)接收到服務(wù)器響應(yīng)時呛梆,說明網(wǎng)絡(luò)請求已經(jīng)連接,默認(rèn)是NSURLSessionResponseAllow表示允許task繼續(xù)執(zhí)行磕诊,進(jìn)行通信和數(shù)據(jù)傳輸填物,如果外部設(shè)置了dataTaskDidReceiveResponse的block,則可以修改通過block修改處理方式霎终。其他的處理方式有:

    NSURLSessionResponseCancel //取消task

    NSURLSessionResponseAllow //允許task繼續(xù)執(zhí)行

    NSURLSessionResponseBecomeDownload //dataTask變成downloadTask

    NSURLSessionResponseBecomeStream //變成NSURLSessionStreamTask

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

    上一個方法的disposition設(shè)置為NSURLSessionResponseBecomeDownload時滞磺,dataTask變成了downloadTask,會觸發(fā)該方法莱褒,需要對將之前delegate關(guān)聯(lián)到新的task击困。代碼注釋如下:

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

    當(dāng)接收服務(wù)端響應(yīng)后并開始傳輸數(shù)據(jù)時,該方法一次或者多次被出發(fā),返回服務(wù)端響應(yīng)的報文數(shù)據(jù)阅茶,在該方法中調(diào)用delegate拼接報文數(shù)據(jù)蛛枚。

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
        didReceiveData:(NSData *)data
    {
    
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
        //delegate處理data數(shù)據(jù)
        [delegate URLSession:session dataTask:dataTask didReceiveData:data];
    
        if (self.dataTaskDidReceiveData) {
            self.dataTaskDidReceiveData(session, dataTask, data);
        }
    }
    

    AFURLSessionManagerTaskDelegate對象負(fù)責(zé)處理響應(yīng)報文數(shù)據(jù)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:方法

    當(dāng)構(gòu)建一個request對象時脸哀,默認(rèn)的緩存屬性策略cachePolicy是NSURLRequestUseProtocolCachePolicy蹦浦,即根據(jù)服務(wù)端響應(yīng)報文的header中的緩存策略決定是否緩存數(shù)據(jù),當(dāng)需要緩存數(shù)據(jù)時撞蜂,會觸發(fā)該方法盲镶,將響應(yīng)報文數(shù)據(jù)緩存到本地,實(shí)現(xiàn)該方法可以修改需要緩存的數(shù)據(jù)蝌诡。

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
     willCacheResponse:(NSCachedURLResponse *)proposedResponse
     completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
    {
        NSCachedURLResponse *cachedResponse = proposedResponse;
    
        if (self.dataTaskWillCacheResponse) { //如果實(shí)現(xiàn)block溉贿,通過block修改緩存數(shù)據(jù)
            cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
        }
        if (completionHandler) { //回調(diào)給系統(tǒng),存儲緩存數(shù)據(jù)
            completionHandler(cachedResponse);
        }
    }
    
NSURLSessionDownloadDelegate
  1. URLSession:downloadTask:didFinishDownloadingToURL:

    執(zhí)行NSURLSessionDownloadTask下載數(shù)據(jù)到本地浦旱,當(dāng)下載完成時宇色,會觸發(fā)該方法,代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
    didFinishDownloadingToURL:(NSURL *)location
    {
        AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
        if (self.downloadTaskDidFinishDownloading) { //如果實(shí)現(xiàn)block闽寡,執(zhí)行下面邏輯
            NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); //獲取需要寫入數(shù)據(jù)的地址fileURL
            if (fileURL) {
                delegate.downloadFileURL = fileURL;
                NSError *error = nil;
                [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]; //將寫入location地址的數(shù)據(jù)移至fileURL
                if (error) {
                    [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
                }
                return;
            }
        }
        if (delegate) { //通過delegate處理下載完成的邏輯
            [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
        }
    }
    

    如果實(shí)現(xiàn)了downloadTaskDidFinishDownloading的block代兵,執(zhí)行block提供寫入下載數(shù)據(jù)的地址fileURL,因?yàn)橹跋到y(tǒng)默認(rèn)將下載數(shù)據(jù)寫入location地址爷狈,會將location的數(shù)據(jù)移至fileURL箩艺。如果沒有實(shí)現(xiàn)block就缆,默認(rèn)調(diào)用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中的數(shù)據(jù)移至downloadFileURL
                [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
            ...  
            }
        }
    }
    

    該方法執(zhí)行downloadTaskDidFinishDownloading獲取需要寫入下載數(shù)據(jù)的地址fileURL一死,在之前的addDelegateForDownloadTask方法中設(shè)置了downloadTaskDidFinishDownloading。然后將默認(rèn)location地址中的下載數(shù)據(jù)移動至fileURL羡微。

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

    執(zhí)行NSURLSessionDownloadTask下載數(shù)據(jù)的過程中谷饿,會觸發(fā)該方法通知下載的進(jìn)度,代碼注釋如下:

    - (void)URLSession:(NSURLSession *)session
          downloadTask:(NSURLSessionDownloadTask *)downloadTask
          didWriteData:(int64_t)bytesWritten
     totalBytesWritten:(int64_t)totalBytesWritten
    totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
    {
        if (self.downloadTaskDidWriteData) { //將進(jìn)度數(shù)據(jù)回調(diào)給外界
            self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
        }
    }
    

    bytesWritten是本次接收到的數(shù)據(jù)妈倔,totalBytesWritten是已經(jīng)接受到數(shù)據(jù)字節(jié)數(shù)博投,totalBytesExpectedToWrite是需要下載的總數(shù)據(jù)量。

總結(jié)

AFURLSessionManager實(shí)現(xiàn)了NSURLSession盯蝴、NSURLSessionTask及其子類的各種代理回調(diào)方法毅哗,同時提供了各種自定義block,增強(qiáng)了代碼邏輯的靈活性捧挺,同時可以監(jiān)聽數(shù)據(jù)傳輸?shù)倪M(jìn)度虑绵,通知外界。該模塊對于網(wǎng)絡(luò)請求的邏輯封裝的比較好闽烙,相關(guān)代碼值得學(xué)習(xí)翅睛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子捕发,更是在濱河造成了極大的恐慌疏旨,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爬骤,死亡現(xiàn)場離奇詭異充石,居然都是意外死亡莫换,警方通過查閱死者的電腦和手機(jī)霞玄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拉岁,“玉大人坷剧,你說我怎么就攤上這事『芭” “怎么了惫企?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長陵叽。 經(jīng)常有香客問我狞尔,道長,這世上最難降的妖魔是什么巩掺? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任偏序,我火速辦了婚禮,結(jié)果婚禮上胖替,老公的妹妹穿的比我還像新娘研儒。我一直安慰自己,他們只是感情好独令,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布端朵。 她就那樣靜靜地躺著,像睡著了一般燃箭。 火紅的嫁衣襯著肌膚如雪冲呢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天招狸,我揣著相機(jī)與錄音敬拓,去河邊找鬼。 笑死瓢颅,一個胖子當(dāng)著我的面吹牛恩尾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挽懦,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼翰意,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起冀偶,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤醒第,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后进鸠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體稠曼,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年客年,在試婚紗的時候發(fā)現(xiàn)自己被綠了霞幅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡量瓜,死狀恐怖司恳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绍傲,我是刑警寧澤扔傅,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站烫饼,受9級特大地震影響猎塞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜杠纵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一荠耽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧淡诗,春花似錦骇塘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至群凶,卻和暖如春插爹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背请梢。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工赠尾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毅弧。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓气嫁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親够坐。 傳聞我的和親對象是個殘疾皇子寸宵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容