OC中的NSURLSession

1. 概述

NSURLSession是在NSURLConnection以後出來的用來取代NSURLConnection進行網絡請求的。NSURLSession並不僅僅指這個類自己,它其實是指代 Foundation框架的URL加載系統中一些列相關的類和協議,它主要由NSURLSessionConfigurationNSURLSessionNSURLSessionTask這三個類及其子類以及相關的協議構成。web

其中NSURLSessionConfiguration用於配置可用網絡、Cookie、安全性、緩存策略、自定義協議、啓動事件等選項,用來對NSURLSession對象進行初始。NSURLSession主要負責網絡的請求與響應。NSURLSessionTask用於建立各類獲取數據、上傳、下載等任務並執行任務。數組

2. NSURLSessionConfiguration

2.1 初始化

NSURLSessionConfiguration對象用於對NSURLSession進行初始化。NSURLSessionConfiguration有3種初始化方式。緩存

2.1.1 第一種
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
複製代碼

這種方式建立的config是一個標準的配置,具備共享NSHTTPCookieStorage、共享NSURLCache、共享NSURLCredentialStorage安全

2.1.2 第二種
NSURLSessionConfiguration *config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
複製代碼

這種方式建立的config不會對緩存、Cookie和證書進行持久性存儲。當這種方式建立的session釋放時或者收到內存警告時或者程序退出時,其緩存的相關信息就會被清除。bash

2.1.3 第三種
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"abc"];
複製代碼

這種方式建立的config用於建立一個能夠後臺執行的session,當咱們須要在程序掛起或退出的狀況下繼續進行上傳或下載任務,就能夠用這種方式來建立。這種方式建立config時須要帶一個惟一標識(identifier),這個標示不能爲nil或空字符串,當程序退出後從新啓動時能夠經過這個標示來恢復未完成的下載或上傳任務。服務器

2.2 屬性介紹

2.2.1 identifier(惟一標識)
@property (nullable, readonly, copy) NSString *identifier;
複製代碼

identifier是隻讀的,只能經過backgroundSessionConfigurationWithIdentifier:這個方法初始化時傳進來,這個標示不能爲nil或空字符串,當程序退出後從新啓動時能夠經過這個標示來恢復未完成的下載或上傳任務。cookie

2.2.2 requestCachePolicy(緩存策略)
@property NSURLRequestCachePolicy requestCachePolicy;
複製代碼

requestCachePolicy是緩存策略,是一個枚舉類型,以下所示:網絡

typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
    // 默認的緩存策略,若是緩存不存在,直接從服務端獲取,若是緩存存在,會根據response中的Cache-Control字段判斷下一步操做,如:Cache-Control字段爲must-revalidata,則詢問服務端該數據時否有更新,無更新的話直接返回給用戶緩存數據,若已更新,則請求服務端。
    NSURLRequestUseProtocolCachePolicy = 0,
    
    // 忽略本地緩存數據,直接請求服務端
    NSURLRequestReloadIgnoringLocalCacheData = 1,
    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
    
    // 忽略本地緩存,代理服務器以及其餘中介,直接請求源服務器
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4,
    
    // 有緩存就用它,無論其有效性(忽略Cache-Control字段),沒有就請求服務器
    NSURLRequestReturnCacheDataElseLoad = 2,
    
    // 只從本地緩存加載,本地緩存沒有就不做操做(無網絡時使用)
    NSURLRequestReturnCacheDataDontLoad = 3,

    // 緩存數據必須獲得服務端確認有效才使用
    NSURLRequestReloadRevalidatingCacheData = 5,
};
複製代碼
2.2.3 timeoutIntervalForRequest(請求超時時間)
@property NSTimeInterval timeoutIntervalForRequest;
複製代碼

用於設置請求超時時長,默認是60s。session

2.2.4 timeoutIntervalForResource(資源超時時間)
@property NSTimeInterval timeoutIntervalForResource;
複製代碼

用於設置資源超時時長,默認是7天,也就是說資源要在7天內到達。app

2.2.5 networkServiceType(網絡傳輸類型)
@property NSURLRequestNetworkServiceType networkServiceType;
複製代碼

指定網絡傳輸類型,通常就用默認的。

typedef NS_ENUM(NSUInteger, NSURLRequestNetworkServiceType)
{
    // 默認值,普通網絡傳輸
    NSURLNetworkServiceTypeDefault = 0,
    
    // 網絡語音通訊傳輸,只能用於IP語音流
    NSURLNetworkServiceTypeVoIP = 1,
    
    // 影像傳輸
    NSURLNetworkServiceTypeVideo = 2,
    
    // 網絡後臺傳輸
    NSURLNetworkServiceTypeBackground = 3,
    
    // 語音傳輸
    NSURLNetworkServiceTypeVoice = 4,
    
    // 指定請求是針對響應性(時間敏感)數據的
    NSURLNetworkServiceTypeResponsiveData = 6, 
    NSURLNetworkServiceTypeAVStreaming = 8 ,
    NSURLNetworkServiceTypeResponsiveAV = 9, 
    NSURLNetworkServiceTypeCallSignaling = 11, 
};
複製代碼
2.2.6 allowsCellularAccess(是否使用蜂窩網絡)
@property BOOL allowsCellularAccess;
複製代碼

指定是否使用蜂窩網絡,默認爲YES。好比在用戶看視頻時沒有開啓WiFi,就能夠提示用戶是否使用流量。

2.2.7 allowsExpensiveNetworkAccess(是否使用昂貴的網絡)
@property BOOL allowsExpensiveNetworkAccess;
複製代碼

這是iOS 13新增的一個屬性,按照字面理解就是是否容許使用昂貴的網絡,在鏈接蜂窩數據或我的熱點時生效,系統自動判斷。(默認是YES)。

2.2.8 allowsConstrainedNetworkAccess(是否使用受限制的網絡)
@property BOOL allowsConstrainedNetworkAccess;
複製代碼

這也是iOS 13新增的一個屬性,按照字面理解就是是否容許使用受限制的網絡,在開啓低流量模式時使用,用戶能夠設置。(默認是YES)。

2.2.9 waitsForConnectivity(是否等待鏈接)
@property BOOL waitsForConnectivity;
複製代碼

這個屬性用於設置是否等待網絡鏈接可用時再執行網絡請求任務,默認是NO。好比我如今只鏈接了蜂窩網絡,可是APP用戶設置的不容許經過蜂窩網絡進行網絡請求,若是waitsForConnectivity這個屬性爲YES,那麼發起請求時就不會立馬返回網絡鏈接失敗的error,而是會等待網絡可用時(好比鏈接上了WiFi)再執行網絡請求,這種狀況下設置的timeoutIntervalForRequest是不生效的,而timeoutIntervalForResource是有效的。

2.2.10 discretionary
@property (getter=isDiscretionary) BOOL discretionary;
複製代碼

用於肯定是否能夠根據系統的判斷來調度後臺任務以得到最佳性能。

2.2.11 sharedContainerIdentifier
@property (nullable, copy) NSString *sharedContainerIdentifier;
複製代碼

若是應用擴展(什麼是應用擴展?)在後臺建立了NSURLSession任務,那就必須設置一個共享容器,以確保應用擴展和載體應用實現數據共享,sharedContainerIdentifier就是用來指定共享容器的標示,而後咱們就能夠經過該標示符獲取到共享容器。

2.2.12 sessionSendsLaunchEvents
@property BOOL sessionSendsLaunchEvents;
複製代碼

用於設置在傳輸完成時是否應該在後臺繼續或啓動應用程序。默認爲YES,而且只有經過+backgroundSessionConfigurationWithIdentifier:建立NSURLSessionConfiguration時纔有效。

2.2.13 connectionProxyDictionary
@property (nullable, copy) NSDictionary *connectionProxyDictionary;
複製代碼

包含有關在此session中使用的代理信息的字典。

2.2.14 TLSMinimumSupportedProtocol
@property SSLProtocol TLSMinimumSupportedProtocol;
複製代碼

請求支持的最低 TLS 版本,默認值是 kSSLProtocol3,即 SSL3.0。

2.2.15 TLSMaximumSupportedProtocol
請求支持的最高 TLS 版本,默認值是 kTLSProtocol12,即 TLS1.2。

@property SSLProtocol TLSMaximumSupportedProtocol;
複製代碼
2.2.16 HTTPShouldUsePipelining
@property BOOL HTTPShouldUsePipelining;
複製代碼

用於開啓 HTTP 流水線(HTTP pipelining),能夠顯着減小請求的加載時間,可是因爲沒有被服務器普遍支持,默認是 NO 的。

2.2.17 HTTPShouldSetCookies
@property BOOL HTTPShouldSetCookies;
複製代碼

指定了請求是否應該使用 Session 存儲的 Cookie,即 HTTPCookieStorage 屬性的值。

2.2.18 HTTPCookieAcceptPolicy
@property NSHTTPCookieAcceptPolicy HTTPCookieAcceptPolicy;

typedef NS_ENUM(NSUInteger, NSHTTPCookieAcceptPolicy) {
    NSHTTPCookieAcceptPolicyAlways, // 接受全部的cookies
    NSHTTPCookieAcceptPolicyNever, // 拒絕全部的cookies
    NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain // 只接受從主文檔中域的cookie
};
複製代碼

決定了什麼狀況下 Session 應該接受從服務器發出的 Cookie。

2.2.19 HTTPAdditionalHeaders
@property (nullable, copy) NSDictionary *HTTPAdditionalHeaders;
複製代碼

是一個字典,包含了須要額外添加到session中的reques請求頭(header),默認爲空。若是在HTTPAdditionalHeaders額外添加的頭部字段與NSURLRequest中重複了,則優先使用NSURLRequest對象中的請求頭部字段。

NSURLSession已經默認給NSURLRequest添加一些請求頭部字段,包括AuthorizationConnectionHostProxy-AuthenticateProxy-AuthorizationWWW-Authenticate

HTTPAddtionalHeaders能夠額外添加的請求頭部字段包括AcceptAccept-LanguageUser-Agent等。

2.2.20 HTTPMaximumConnectionsPerHost
@property NSInteger HTTPMaximumConnectionsPerHost;
複製代碼

指定session內能夠同時鏈接一個主機的最大鏈接數。

2.2.21 HTTPCookieStorage
@property (nullable, retain) NSHTTPCookieStorage *HTTPCookieStorage;
複製代碼

存儲了session所使用的cookie。經過defaultSessionConfigurationbackgroundSessionConfigurationWithIdentifier:建立的config會使用NSHTTPCookieStorage+ sharedHTTPCookieStorage單例,要清除存儲的cookie,直接set爲nil便可。經過ephemeralSessionConfiguration建立的config``cookie僅僅儲存到內存,session失效時會自動清除。

2.2.22 URLCredentialStorage
@property (nullable, retain) NSURLCredentialStorage *URLCredentialStorage;
複製代碼

證書存儲。經過defaultSessionConfigurationbackgroundSessionConfigurationWithIdentifier:建立的config會使用NSURLCredentialStorage+ sharedCredentialStorage單例,要清除存儲的證書,直接set爲nil便可。經過ephemeralSessionConfiguration建立的config證書僅僅儲存到內存,session失效時會自動清除。

2.2.23 URLCache
@property (nullable, retain) NSURLCache *URLCache;
複製代碼

緩存NSURLRequest的response。默認的configuration,默認值的是sharedURLCache,後臺的configuration,默認值是nil。短暫的configuration,cache存於內存,session失效,cache自動清除。

2.2.24 shouldUseExtendedBackgroundIdleMode
@property BOOL shouldUseExtendedBackgroundIdleMode;
複製代碼

爲建立的任何tcp套接字啓用擴展後臺空閒模式。 啓用此模式會要求系統保持打開狀態,並在進程移動到後臺時延遲迴收。

2.2.25 protocolClasses
@property(copy) NSArray<Class> *protocolClasses;
複製代碼

用來配置特定某個session所使用的自定義協議(該協議是 NSURLProtocol 的子類)的數組。

2.2.26 multipathServiceType
@property NSURLSessionMultipathServiceType multipathServiceType;

typedef NS_ENUM(NSInteger, NSURLSessionMultipathServiceType)
{
    NSURLSessionMultipathServiceTypeNone = 0, // 不使用多路徑TCP服務(默認)
    NSURLSessionMultipathServiceTypeHandover = 1, // 多路徑TCP服務,提供Wi-Fi和蜂窩之間的無縫切換,以保持鏈接。
    NSURLSessionMultipathServiceTypeInteractive = 2, // 嘗試使用最低延遲接口的服務
    NSURLSessionMultipathServiceTypeAggregate = 3 //聚合其餘多路徑選項容量的服務,旨在提升吞吐量和最小化延遲。
}
複製代碼

指定經過 Wi-Fi 和 蜂窩網絡傳輸數據的多路徑 TCP 的鏈接策略。

3. NSURLSession

咱們可使用NSURLSession的API來建立一個或多個session對象,每一個session對象均可以管理多個網絡請求任務。建立會話(session)的方式有3種:

3.1 獲取全局session

NSURLSession *session = [NSURLSession sharedSession];
複製代碼

經過這種方式獲取全局的NSURLSession對象。

3.2 指定NSURLSessionConfiguration建立session

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
複製代碼

先建立一個NSURLSessionConfiguration對象config,而後根據config來建立session

3.3 建立帶代理的session

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config
                                                          delegate:self
                                                     delegateQueue:[NSOperationQueue new]];
複製代碼

這種方式一樣須要一個NSURLSessionConfiguration對象,而且要遵循NSURLSessionDelegate協議。

4 NSURLSessionTask

NSURLSessionTask是一個抽象類,它包含3個子類:NSURLSessionDataTaskNSURLSessionUploadTaskNSURLSessionDownloadTask。它們的繼承關係以下:

NSURLSessionTask繼承關係

NSURLSessionTask的數據返回方式主要有兩種,一種是回調(completionHandler),另外一種是代理。不論是哪種task,其建立都是基於NSURLSession對象,建立task後必定要調用[task resume]來啓動任務。

4.1 NSURLSessionDataTask

NSURLSessionDataTask繼承自NSURLSessionTask,主要用於讀取服務端的簡單數據,好比JSON數據。建立NSURLSessionDataTask有4種方式,分別是基於NSURLNSURLRequest對象來建立,每種又分爲帶回調和不帶回調2種。

NSURL *url = [NSURL URLWithString:@"http://aaa.bbb/test/login"];
    
    // 注意,請求對象內部默認已經包含了請求頭和請求方法(GET)
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    NSURLSession *session = [NSURLSession sharedSession];
    
    // 第一種:基於NSURL建立不帶回調的task
    NSURLSessionDataTask *task1 = [session dataTaskWithURL:url];
    
    // 第二種:基於NSURLRequest建立不帶回調的task
    NSURLSessionDataTask *task2 = [session dataTaskWithRequest:request];
    
    // 第三種:基於NSURL建立帶回調的task
    NSURLSessionDataTask *task3 = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
    }];
    
    // 第四種:基於NSURLRequest建立帶回調的task
    NSURLSessionDataTask *task4 = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
    }];
    
    // 每一個task都要調用resume方法纔會執行task
    [task1 resume];
複製代碼

4.2 NSURLSessionUploadTask

NSURLSessionUploadTask繼承自NSURLSessionDataTask,主要用於 向服務器上傳文件類型的數據。有5種建立任務的方法。

// 第1、二種,經過文件路徑上傳(一種帶回調一種不帶回調),這種方式上傳不須要把文件內容所有加載到內存中
- (void)NSURLSessionUploadTask1{
    NSURL *fileUrl = [NSURL fileURLWithPath:@"/user/file/abc.txt"];
    
    NSURL *url = [NSURL URLWithString:@"http://aaa.bbb/test/upload"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
    
    NSURLSession *session = [NSURLSession sharedSession];
    
    // 不帶回調
    NSURLSessionUploadTask *task  = [session uploadTaskWithRequest:request fromFile:fileUrl];
    
    // 帶回調
    NSURLSessionUploadTask *task  = [session uploadTaskWithRequest:request fromFile:fileUrl completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
    }];
    
    [task resume];
}
複製代碼
// 第3、四種,上傳NSData(帶回調和不帶回調兩種),這種方式上傳文件時須要先將文件加載到內存中
- (void)NSURLSessionUploadTask2{
    NSData *data = [NSData dataWithContentsOfFile:@"/user/file/abc.txt"];
    
    NSURL *url = [NSURL URLWithString:@"http://aaa.bbb/test/upload"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)data.length] forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
    
    NSURLSession *session = [NSURLSession sharedSession];
    
    // 不帶回調
    NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:data];
    
    // 帶回調
    NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
    }];
    
     [task resume];
       
}
複製代碼
// 第五種,經過表單上傳,將要上傳的數據二進制寫入httpbody中,而後將其添加到請求頭中
- (void)NSURLSessionUploadTask3{
    
    NSURL *url = [NSURL URLWithString:@"http://aaa.bbb/test/upload"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    request.HTTPBodyStream = [NSInputStream inputStreamWithFileAtPath:@"/user/file/abc.txt"];
    [request setValue:[NSString stringWithFormat:@"%lu", (unsigned long)data.length] forHTTPHeaderField:@"Content-Length"];
    [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
    
    
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionUploadTask *task = [session uploadTaskWithStreamedRequest:request];
    [task resume];
}
複製代碼

4.3 NSURLSessionDownloadTask

NSURLSessionDownloadTask繼承自NSURLSessionTask,主要用於文件下載。有6種建立下載任務的方式。須要注意的是文件下載保存的是一個臨時文件,下載完後要將其拷貝到本身想要存儲的目錄再刪除臨時文件。

- (void)NSURLSessionDownloadTaskTest{
    NSString *urlStr = @"http://aaa.bbb/test/downloadFile.zip";
    NSURL *url = [NSURL URLWithString:urlStr];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
   
    
    // 不須要後臺下載的話就用其餘方式初始化config
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"abcd"];
    
    NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
    
    // 第一種,經過一個NSURL對象來建立task(不帶回調)
    NSURLSessionDownloadTask *task1 = [session downloadTaskWithURL:url];
    
    // 第二種,經過一個NSURL對象來建立task(帶回調)
    NSURLSessionDownloadTask *task2 = [session downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
    }];
    
    // 第三種,經過一個NSURLRequest對象來建立task(不帶回調)
    NSURLSessionDownloadTask *task3 = [session downloadTaskWithRequest:request];
    
    // 第四種,經過一個NSURLRequest對象來建立task(帶回調)
    NSURLSessionDownloadTask *task4 = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
    }];
    
    // 第五種,經過一個resumeData(保存在沙盒中的下載進度)數據來建立task(不帶回調)
    NSString *resumePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"123.tmp"];
    NSData *resumeData = [NSData dataWithContentsOfFile:resumePath];
    NSURLSessionDownloadTask *task5 = [session downloadTaskWithResumeData:resumeData];

    // 第六種,經過一個resumeData數據來建立task(帶回調)
    NSURLSessionDownloadTask *task6 = [session downloadTaskWithResumeData:resumeData completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
    }];
    
    // 啓動任務
    [task1 resume];
}
複製代碼

當暫停下載時會產生一個記錄下載進度的數據resumeData經過回調傳過來,咱們要將這個數據保存在沙盒中,以便繼續下載時能夠經過這個數據來建立下載任務(斷點續傳)。

// 暫停下載
- (void)pauseDownload {
    [self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
        // 記錄已下載的數據
        // 把下載進度數據保存到沙盒中
        NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"123.tmp"];
        [self.resumData writeToFile:path atomically:YES];
    }];
}
複製代碼

5. NSURLSessionDelegate

相關代理主要有四個:NSURLSessionDelegateNSURLSessionTaskDelegateNSURLSessionDataDelegateNSURLSessionDownloadDelegate。它們的繼承關係以下:

NSURLSessionDelegate繼承關係

5.1 NSURLSessionDelegate

NSURLSessionDelegate用於處理session級別的事件,好比session的生命週期和服務端請求驗證客戶端的身份或證書。

5.1.1 處理session的生命週期

- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error;
複製代碼

session失效時會調用這個代理方法,有如下兩種方法使session失效。

  • - (void)finishTasksAndInvalidate;session將等到全部task結束或失敗後才調用這個委託方法。
  • - (void)invalidateAndCancel;session將直接取消全部正在執行的task,當即調用此委託方法。

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session;
複製代碼

若是是經過backgroundSessionConfigurationWithIdentifier:方式建立的configuration,再經過這個configuration建立的session用於管理能夠後臺執行的上傳或下載任務,當後臺傳輸完成時就會調用上面這個代理方法。

當後臺傳輸完成時,若是此時APP處於未啓動狀態,那麼APP會自動在後臺從新啓動,啓動後會調用UIApplicationDelegate中下面這個代理方法,裏面包含一個會話標示符(identifier)和completionHandler,咱們首先要將completionHandler保存起來,而後根據identifier從新生成configuration,而後經過這個configuration建立的session將從新關聯對應的後臺任務。

// UIApplicationDelegate中的方法
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler;
複製代碼
5.1.2 處理session身份驗證

當服務器請求客戶端證書或請求驗證NTLM身份驗證時,或者使用SSL或TLS鏈接服務器時都會調用下面這個代理方法。

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{
    /*
     challenge是一個封裝了服務端發過來的驗證請求的對象,經過下面這個方法能夠獲取驗證方式,驗證方式有以下幾種狀況:
     NSURLAuthenticationMethodClientCertificate 表示要求驗證客戶端的證書
     NSURLAuthenticationMethodNegotiate 使用Kerberos或NTLM身份驗證
     NSURLAuthenticationMethodNTLM 使用NTLM身份驗證
     NSURLAuthenticationMethodServerTrust 驗證服務端提供的證書
     */
    NSString *authenticationMethod = [challenge.protectionSpace authenticationMethod];
    
    /*
     completionHandler回調要傳2個參數:
     第一個參數是一個枚舉:
     typedef NS_ENUM(NSInteger, NSURLSessionAuthChallengeDisposition) {
         NSURLSessionAuthChallengeUseCredential = 0, // 指明經過另外一個參數 credential 提供證書
         NSURLSessionAuthChallengePerformDefaultHandling = 1, // 至關於未執行代理方法,使用默認的處理方式,不使用參數 credential
         NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2, // 取消整個請求,提供的憑證參數被忽略
         NSURLSessionAuthChallengeRejectProtectionSpace = 3, // 拒絕該 protectionSpace 的驗證,不使用參數 credential
     }

     第二個參數:
     第二個參數爲NSURLCredential類型,它是一種身份驗證憑證,包含特定於憑證類型的信息和用於使用的持久存儲類型。
     當第一個參數爲NSURLSessionAuthChallengeUseCredential時,第二個參數必須傳身份驗證的憑據,不然就傳nil;
        經過NSURLCredential能夠建立3種類型的credential:
        1.當authenticationMethod的值爲NSURLAuthenticationMethodHTTPBasic 或 NSURLAuthenticationMethodHTTPDigest時,調用如下方法來建立:
        - (instancetype)initWithUser:(NSString *)user password:(NSString *)password persistence:(NSURLCredentialPersistence)persistence;
        + (NSURLCredential *)credentialWithUser:(NSString *)user password:(NSString *)password persistence:(NSURLCredentialPersistence)persistence;
        2.當authenticationMethod的值爲NSURLAuthenticationMethodClientCertificate時,調用如下方法來建立:
        - (instancetype)initWithIdentity:(SecIdentityRef)identity certificates:(nullable NSArray *)certArray persistence:(NSURLCredentialPersistence)persistence;
        + (NSURLCredential *)credentialWithIdentity:(SecIdentityRef)identity certificates:(nullable NSArray *)certArray persistence:(NSURLCredentialPersistence)persistence;
        3.當authenticationMethod的值爲NSURLAuthenticationMethodServerTrust時,調用如下方法來建立:
        - (instancetype)initWithTrust:(SecTrustRef)trust;
        + (NSURLCredential *)credentialForTrust:(SecTrustRef)trust;

     */
}
複製代碼

若是上面這個代理方法沒有實現的話就會調用NSURLSessionTaskDelegate的下面這個方法:

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                            didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 
                              completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
    /*
     這裏的authenticationMethod有如下幾種可能
     NSURLAuthenticationMethodDefault 默認的驗證
     NSURLAuthenticationMethodHTMLForm 不會用於 URL Loading System,在經過 web 表單驗證時可能用到
     NSURLAuthenticationMethodHTTPBasic 基本的 HTTP 驗證,經過 NSURLCredential 對象提供用戶名和密碼,至關於 Default 默認的驗證
     NSURLAuthenticationMethodHTTPDigest 相似於基本的 HTTP 驗證,摘要會自動生成,一樣經過 NSURLCredential 對象提供用戶名和密碼
     */
    NSString *authenticationMethod = [challenge.protectionSpace authenticationMethod];                              
}
複製代碼

5.2 NSURLSessionTaskDelegate

NSURLSessionTaskDelegate用於處理task級別的事件。

5.2.1 處理task生命週期
- (void)URLSession:(NSURLSession *)session 
              task:(NSURLSessionTask *)task 
didCompleteWithError:(NSError *)error;
複製代碼

當任務完成時會回到這個方法。要注意的是這裏的error只有客戶端的錯誤,好比沒法解析主機名或鏈接到主機。服務端的錯誤是不會經過這個error傳過來的。

5.2.2 處理task的重定向
- (void)URLSession:(NSURLSession *)session 
              task:(NSURLSessionTask *)task 
willPerformHTTPRedirection:(NSHTTPURLResponse *)response 
        newRequest:(NSURLRequest *)request 
 completionHandler:(void (^)(NSURLRequest *))completionHandler;
複製代碼

當服務器請求HTTP重定向時會調用這個方法。注意這個方法只適用於defaultSessionConfigurationephemeralSessionConfigurationsession生成的sessionbackgroundSessionConfigurationWithIdentifier:生成的session會自動重定向,而不會走這個方法。

  • response是服務器對原始請求的響應
  • request是新的NSURLRequest
  • completionHandler回調能夠傳一個重定向的NSURLRequest對象,也能夠傳nil,傳nil的話就不進行重定向。
5.2.3 處理上傳task
- (void)URLSession:(NSURLSession *)session 
              task:(NSURLSessionTask *)task 
   didSendBodyData:(int64_t)bytesSent 
    totalBytesSent:(int64_t)totalBytesSent 
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
複製代碼

執行上傳任務時,會按期調用這個代理方法來告知上傳進度。

  • bytesSent是從上次調用這個方法開始到此次調用這個方法期間發送的字節數。
  • totalBytesSent是已經發送的總的字節數。
  • totalBytesExpectedToSend是要上傳的數據的總長度。

- (void)URLSession:(NSURLSession *)session 
              task:(NSURLSessionTask *)task 
 needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler;
複製代碼

當咱們經過uploadTaskWithStreamedRequest:這個方法來建立任務時,必需要實現這個代理方法,當調用這個方法時,須要經過completionHandler回調傳入NSInputStream對象。由於上傳可能失敗,當失敗時就會調用這個代理方法,就須要在這個方法裏面從新提供請求體流。

5.2.4 處理身份認證
- (void)URLSession:(NSURLSession *)session 
              task:(NSURLSessionTask *)task 
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;
複製代碼

這個代理方法用於處理task級別的身份驗證,當session級別的身份認證代理函數沒有實現的話就會走這個代理,具體請求前面已經介紹過了,這裏再也不贅述。

5.2.5 處理延遲和等待任務
- (void)URLSession:(NSURLSession *)session 
              task:(NSURLSessionTask *)task 
willBeginDelayedRequest:(NSURLRequest *)request 
 completionHandler:(void (^)(NSURLSessionDelayedRequestDisposition disposition, NSURLRequest *newRequest))completionHandler;
 
 // disposition的枚舉類型
 typedef NS_ENUM(NSInteger, NSURLSessionDelayedRequestDisposition) {
    NSURLSessionDelayedRequestContinueLoading = 0, // 繼續執行原始請求
    NSURLSessionDelayedRequestUseNewRequest = 1, // 使用新請求執行下載
    NSURLSessionDelayedRequestCancel = 2, // 取消該任務
}
複製代碼

當一個延遲啓動的任務(好比設置了earliestBeginDate這個屬性)準備啓動時就會調用這個代理方法。只有在等待網絡加載而且須要被新的請求替換時才須要實現這個委託方法。

經過completionHandler回調告訴task如何處理。當dispositionNSURLSessionDelayedRequestUseNewRequest時須要同時傳一個新的NSURLRequest對象。


- (void)URLSession:(NSURLSession *)session 
taskIsWaitingForConnectivity:(NSURLSessionTask *)task;
複製代碼

NSURLSessionConfigurationwaitsForConnectivity屬性設置爲YES而且沒法得到足夠的鏈接性時就會調用這個代理方法。好比當前用戶只連了蜂窩網絡,但用戶又設置了只能在WiFi鏈接時才能看視頻,當用戶播放視頻時就會調用這個代理方法,那咱們就能夠在這個代理方法裏面更新UI提示用戶鏈接WiFi。要注意每一個任務最多調用此方法一次,而且只在鏈接最初不可用時調用。

5.2.6 收集任務的執行狀況
- (void)URLSession:(NSURLSession *)session 
              task:(NSURLSessionTask *)task 
didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics;
複製代碼

當任務執行完成後,能夠經過這個方法知道任務的執行狀況,好比任務執行的時長、重定向次數等信息放在NSURLSessionTaskMetrics對象裏面。

5.3 NSURLSessionDataDelegate

NSURLSessionDataDelegate繼承自NSURLSessionTaskDelegate,主要用來處理dataTask數據(好比接收到響應,接收到數據,是否緩存數據等)。

5.3.1 處理task生命週期
- (void)URLSession:(NSURLSession *)session 
          dataTask:(NSURLSessionDataTask *)dataTask 
didReceiveResponse:(NSURLResponse *)response 
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;
 
 // disposition的枚舉類型
 typedef NS_ENUM(NSInteger, NSURLSessionResponseDisposition) {
    NSURLSessionResponseCancel = 0, // 該task會被取消
    NSURLSessionResponseAllow = 1, // 該task正常進行
    NSURLSessionResponseBecomeDownload = 2, // 轉成一個downloadTask
    NSURLSessionResponseBecomeStream // 轉成一個StreamTask
}
複製代碼

DataTask收到響應時,會調用這個代理方法。咱們須要經過completionHandler回調指明任務後續執行方式。

  • 當傳入的dispositionNSURLSessionResponseBecomeDownload時,是將task轉換爲download task,同時會調用URLSession:dataTask:didBecomeDownloadTask:這個代理方法。
  • 當傳入的dispositionNSURLSessionResponseBecomeStream時,是將task轉換爲StreamTask,同時會調用URLSession:dataTask:didBecomeStreamTask:這個代理方法。

當咱們的requestcontent-type支持multipart/x-mixed-replace 時,服務器會將數據分片傳回來,並且每次傳回來的數據會覆蓋以前的數據。每次返回新的數據時,都會調用這個方法個,咱們應該在這個函數中合理地處理先前的數據,不然會被新數據覆蓋。若是咱們沒有提供該方法的實現,那麼session將會繼續任務,也就是說會覆蓋以前的數據。


- (void)URLSession:(NSURLSession *)session 
          dataTask:(NSURLSessionDataTask *)dataTask 
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask;
複製代碼

上面已經介紹了何時會調用這個代理方法。


- (void)URLSession:(NSURLSession *)session 
          dataTask:(NSURLSessionDataTask *)dataTask 
didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask;
複製代碼

上面已經介紹了何時會調用這個代理方法。

5.3.2 接收數據
- (void)URLSession:(NSURLSession *)session 
          dataTask:(NSURLSessionDataTask *)dataTask 
    didReceiveData:(NSData *)data;
複製代碼

當咱們收到數據時就會調用這個代理方法,這個方法個會被屢次調用,咱們須要將每次收到的數據拼接起來獲得完整的數據。

5.3.3 處理緩存
- (void)URLSession:(NSURLSession *)session 
          dataTask:(NSURLSessionDataTask *)dataTask 
 willCacheResponse:(NSCachedURLResponse *)proposedResponse 
 completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler;
複製代碼

task接收到全部指望的數據後,session會調用此代理方法。若是沒有實現該方法,那麼就會使用建立session時使用的configuration對象的緩存策略。

5.4 NSURLSessionDownloadDelegate

NSURLSessionDownloadDelegate繼承自NSURLSessionTaskDelegate,主要用來處理下載任務。

5.4.1 處理下載任務的生命週期
- (void)URLSession:(NSURLSession *)session 
      downloadTask:(NSURLSessionDownloadTask *)downloadTask 
didFinishDownloadingToURL:(NSURL *)location;
複製代碼

當下載任務完成後會調用這個代理方法,這個方法是必現實現的。location是下載文件保存的路徑,要注意的是咱們必如今這個方法裏面將下載的文件保存到咱們想要保存的目錄去,由於這裏返回的是一個臨時路徑,這個方法結束後這個路徑下的文件就會被刪除。另外,若是讀取文件內容的話,須要在子線程執行,不然可能會形成頁面卡死。

5.4.2 恢復下載
- (void)URLSession:(NSURLSession *)session 
      downloadTask:(NSURLSessionDownloadTask *)downloadTask 
 didResumeAtOffset:(int64_t)fileOffset 
expectedTotalBytes:(int64_t)expectedTotalBytes;
複製代碼

當下載被取消或者下載失敗後從新恢復下載時會調用這個代理方法。注意fileOffset這個參數,若是文件緩存策略或者文件最後更新日期阻止重用已經下載的文件內容,那麼該值爲0。不然,該值表示當前已經下載data的偏移量,那就會從這個偏移量開始下載,已經下載的不會從新下載。

恢復下載時經過downloadTaskWithResumeData:這個方法個來建立任務,那這個resumeData從哪裏獲取呢?前面講過一種狀況,就是當用戶手動取消下載時能夠將返回的resumeData存入沙盒,恢復下載時再從沙盒去獲取。

下面咱們來介紹一下另外一種狀況,當下載失敗時會調用-URLSession: task: didCompleteWithError:這個代理方法,這個方法的error信息裏面是攜帶了resumeData的,能夠從errorUserInfo字典中經過NSURLSessionDownloadTaskResumeData這個鍵來獲取resumeData

5.4.3 接收進度更新
- (void)URLSession:(NSURLSession *)session 
      downloadTask:(NSURLSessionDownloadTask *)downloadTask 
      didWriteData:(int64_t)bytesWritten 
 totalBytesWritten:(int64_t)totalBytesWritten 
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
複製代碼

下載任務執行過程當中週期性調用這個代理方法來通知下載進度。

  • bytesWritten表示從上次調用這個方法開始到此次調用這個方法個期間接收到的數據字節數。
  • totalBytesWritten表示已經接收到的總字節數。
  • totalBytesExpectedToWrite表示要下載的數據總長度。
相關文章
相關標籤/搜索