使用NSURLSession(蘋果官方文檔翻譯)

NSURLSession類以及相關類為通過HTTP下載資源提供了一個API.這個API提供了一系列豐富的代理集合,這些代理方法支持證書和權限管理以及當你的app不再運行或暫停時能夠讓你的app支持后臺下載。
要使用NSURLSession接口,你的app需要創(chuàng)建一系列的會話sessions,每一個session都能夠協(xié)調一堆相關的數(shù)據(jù)傳輸任務。例如,如果你正在寫一個web瀏覽器,你的app需要為每個標簽tab或窗口window創(chuàng)建一個session,你的app添加了一系列任務,而每一個任務都代表了一個指定URL的請求(and for any follow-on URLs if the original URL returned an HTTP redirect)。
像大多數(shù)網(wǎng)絡APIs,NSURLSession APIs是高度異步的。當數(shù)據(jù)傳輸完成后,這些數(shù)據(jù)傳輸可能是成功也可能是失敗的,如果你使用系統(tǒng)默認的代理,那就要利用block進行處理。當然,你也可以選擇性的自定義代理方法,這樣任務對象task objects就可以調用代理方法處理接收服務器返回的數(shù)據(jù)(或者數(shù)據(jù)傳輸完成時,用于文件下載)。

提示:完成回調主要是為了替代使用自定義委托, 如果你實現(xiàn)了回調方法,那么你就不能再使用代理接收數(shù)據(jù)。

除了給代理傳遞信息,NSURLSession接口還提供了狀態(tài)status和進度progress屬性。它支持取消任務、重啟任務和暫停任務。它有能夠恢復暫停、取消以及因下載失敗的暫停(這段翻譯的感覺不是特別爽,原文這樣,翻譯的不對的,希望大家指出The NSURLSession API provides status and progress properties, in addition to delivering this information to delegates. It supports canceling, restarting (resuming), and suspending tasks, and it provides the ability to resume suspended, canceled, or failed downloads where they left off.

理解URL Session的概念

一個會話Session中,任務tasks的行為,取決于三件事情:

  • 會話Session的類型 (由用來創(chuàng)建它的configuration對象的類型決定)

  • 任務task的類型

  • 任務task創(chuàng)建時,應用程序是否在前臺

會話sessions的類型

NSURLSessionAPI 支持三種類型的會話session,每種會話session都由創(chuàng)建它的配置對象configuration object決定

  • 默認會話:默認的會話sessions類似其他的用來downloading URLsFoundation方法,他們基于硬盤緩存和在用戶鑰匙鏈中的持久化存儲證書進行存儲。
  • 臨時性會話:臨時性會話并不在硬盤上存儲數(shù)據(jù),所有的緩存、證書存儲等都存儲到內存RAM上并且和會話相關聯(lián)(這句總覺得別扭)。
  • 后臺會話:除了是一個單獨的進程處理所有的數(shù)據(jù)傳輸外,后臺會話很類似于默認會話。后臺會話有許多限制,下面會做闡述。

任務Tasks的類型

在一個會話中,NSURLSession類支持三種任務Task類型,分別是:數(shù)據(jù)任務(data tasks)、下載任務(download tasks)和上傳任務(upload tasks)。

  • 1 數(shù)據(jù)任務:數(shù)據(jù)任務是用NSData對象發(fā)送和接收數(shù)據(jù)。數(shù)據(jù)任務是用于經常和服務器進行交互的較短的應用端(App端)請求。每次接收到一塊數(shù)據(jù),數(shù)據(jù)任務就可以立即通過回調將一塊一塊(或者叫一片一片)數(shù)據(jù)返回給app。
  • 2 下載任務:下載任務是以文件file的形式獲取數(shù)據(jù),當應用程序不再運行時支持后臺下載。
  • 3 上傳任務:上傳任務是以文件file的形式上傳數(shù)據(jù),當應用程序不再運行時,支持后臺上傳。

后臺傳輸數(shù)據(jù)的注意事項

當應用程序暫停時,NSURLSession類支持后臺傳輸數(shù)據(jù)。只有通過后臺會話配置對象(background session configuration object)創(chuàng)建的會話(sessions)才支持后臺傳輸數(shù)據(jù)(通過調用這個方法+ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier創(chuàng)建后臺會話配置對象)。
對于后臺會話而言,因為實際傳輸?shù)臄?shù)據(jù)是通過一個獨立進程進行的,也因為重啟一個應用的進程所需要的代價太大,因此一些功能不能使用,導致以下限制:

  • 1 會話需要用一個代理接收事件(在上傳和下載方面,代理在處理數(shù)據(jù)傳輸時是相同的);
  • 2 只有HTTPHTTPS協(xié)議支持后臺數(shù)據(jù)傳輸(不支持自定義協(xié)議)
  • 3 要有重定向(Redirects are always followed);
  • 4 只支持文件file上傳(程序退出后不支持數(shù)據(jù)data objects上傳和流stream上傳);
  • 5 當應用進入后臺,如果開啟后臺數(shù)據(jù)傳輸,配置對象configuration objectdiscretionary屬性被看做是true
提示:在iOS 8 和 OS X 10.10之前,數(shù)據(jù)任務(data tasks)不支持后臺傳輸數(shù)據(jù)

在iOS和OS X系統(tǒng)上,在應用重啟方面稍微有點不同。
在iOS上,如果一個應用停止運行,那么當后臺數(shù)據(jù)傳輸完成或者請求認證時,iOS會在應用的UIApplicationDelegate對象里調用application:handleEventsForBackgroundURLSession:completionHandler:方法去重啟你的應用。這個方法提供了能夠讓你的app重啟的標識identifier。你的應用應該存儲這個完成回調,用同一個標識identifier創(chuàng)建一個后臺配置對象,同時根據(jù)對應的配置對象configuration object創(chuàng)建一個會話session。這個新的會話能夠自動的和后臺活動重新關聯(lián)。之后,當會話完成后臺下載任務時,它就會調用代理方法URLSessionDidFinishEventsForBackgroundURLSession:,在這個代理方法中,你可在主線程調用前面存儲的完成回調,這樣就可以讓操作系統(tǒng)知道此時可以安全的掛起suspend你的應用了。
在iOS和OS X上,當用戶重啟應用時,你的應用應該立刻創(chuàng)建和上次最后運行時尚未完成的那些會話sessions的任務tasks相同的后臺配置對象,然后再根據(jù)對應的配置對象創(chuàng)建相應的會話。這些會話sessions同樣能夠自動的和正在進行的后臺活動自動關聯(lián)。

提示:每個標識只能創(chuàng)建一個會話(當你創(chuàng)建這個指定的配置對象時),多個會話共享同一個標識符的行為是不明確的(這句翻譯咋還是那么別扭呢?)

當你的應用掛起suspended時,如果一個任務完成,那么就會調用代理方法URLSession:downloadTask:didFinishDownloadingToURL:來處理對應任務的下載文件。
類似的,如果任務是請求認證時,NSURLSession對象會視情況的調用URLSession:task:didReceiveChallenge:completionHandler:或者URLSession:didReceiveChallenge:completionHandler:進行處理。
后臺會話中,上傳和下載任務在網(wǎng)絡出錯時能夠通過URL加載系統(tǒng)自動的進行重定向。沒有必要利用reachabilityAPIs決定何時重試失敗的任務。

舉一個如何利用NSURLSession進行后臺數(shù)據(jù)傳遞的例子,可以看這個簡單的例子

生命周期和代理交互

在你使用NSURLSession類的基礎上,會對你完全理解session的生命周期,包括如何和代理進行交互,代理調用的順序,當服務器返回一個重定向的時候發(fā)生了什么,當下載失敗的時候發(fā)生了什么等等可能有很大幫助。
為了完全闡述URL session的生命周期,看這個文檔
NSCopying Behavior

NSCopying行為

會話session和任務對象task objects遵守以下NSCopying協(xié)議:

  • 當你的app拷貝一個會話session和任務task對象時,你得到的是同一個對象。
  • 當你的app拷貝一個配置對象configuration object時,你會得到一個可以自由修改的新的副本。

代理類接口

@import Foundation;
NS_ASSUME_NONNULL_BEGIN
typedef void (^CompletionHandler)();
@interface MySessionDelegate : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSURLSessionStreamDelegate>
@property NSMutableDictionary <NSString *, CompletionHandler>*completionHandlers;
@end
NS_ASSUME_NONNULL_END

創(chuàng)建和配置一個會話

NSURLSessionAPI提供了一個寬泛的配置選擇:

  • 在某種程度上,對于指定的一個會話,個人存儲支持緩存、cookies、證書以及協(xié)議
  • 綁定到一個請求或一組請求的認證(Authentication, tied to a specific request (task) or group of requests (session)
  • 通過URL上傳和下載文件,鼓勵數(shù)據(jù)(文件的內容)和元數(shù)據(jù)(the URL and settings)分離。
  • 每個主機最大連接數(shù)的配置
  • 如果一個完整的資源在一定的時間內不能完全下載,那么每個資源的超時設定會觸發(fā)(翻譯的別扭,原話是這樣Per-resource timeouts that are triggered if an entire resource cannot be downloaded in a certain amount of time
  • TLS版本支持的最大和最小值
  • 自定義代理字典(Custom proxy dictionaries
  • 控制cookie策略(Control over cookie policies
  • Control over HTTP pipelining behavior(不知道咋翻譯)
    因為大部分設置都包含在一個單獨的配置對象,您可以重用常用的設置。實例化一個會話對象時,指定以下:
  • 有一個管理會話行為和會話任務的配置對象
  • 當接收和處理指定會話和會話任務的事件時,處理到來數(shù)據(jù)的代理對象可以選擇性的決定是否一個資源加載請求能夠轉化為下載,等等。
    如果你不提供代理方法,那么系統(tǒng)將使用自帶的代理,通過這種方法你就可以和容易的使用NSURLSession替代使用sendAsynchronousRequest:queue:completionHandler:方法的代碼(疑問?)
提示:如果你的app需要在后臺傳輸數(shù)據(jù),那么需要自定義代理方法。

你初始化session對象后,如果你不再創(chuàng)建一個新的session,那么你就不能改變配置和代理。
1-2展示了如何創(chuàng)建普通、臨時和后臺會話

// Creating session configurations
NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSessionConfiguration *ephemeralConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSessionConfiguration *backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier: @"com.myapp.networking.background"];
 // Configuring caching behavior for the default session
NSString *cachesDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
NSString *cachePath = [cachesDirectory stringByAppendingPathComponent:@"MyCache"];
/* Note:
 iOS requires the cache path to be
 a path relative to the ~/Library/Caches directory,
 but OS X expects an absolute path.
 */
#if TARGET_OS_OSX
cachePath = [cachePath stringByStandardizingPath];
#endif
NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:16384 diskCapacity:268435456 diskPath:cachePath];
defaultConfiguration.URLCache = cache;
defaultConfiguration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
// Creating sessions
id <NSURLSessionDelegate> delegate = [[MySessionDelegate alloc] init];
NSOperationQueue *operationQueue = [NSOperationQueue mainQueue];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfiguration delegate:delegate operationQueue:operationQueue];
NSURLSession *ephemeralSession = [NSURLSession sessionWithConfiguration:ephemeralConfiguration delegate:delegate delegateQueue:operationQueue];
NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfiguration delegate:delegate delegateQueue:operationQueue];

除了背景配置外,你可以使用其他的會話配置對象在創(chuàng)建額外的會話sessions(你不能重用背景會話配置,因為如果用一個背景會話配置對象創(chuàng)建了兩個背景會話對象后,這樣這兩個背景會話對象就會共享一個難以識別的 標識了)。你也可以在任意時間安全的修改配置對象configuration objects。當你創(chuàng)建一個會話session,會話session會對配置對象configuration object進行深拷貝,因此修改配置對象configuration object只會影響用它再創(chuàng)建的新對象,不會影響原來的對象。例如,你用改配置對象又創(chuàng)建了一個新會話session,這個新會話只有在wifi狀態(tài)下才接收數(shù)據(jù)內容,如下面1-3列表所示。

ephemeralConfiguration.allowsCellularAccess = NO;
NSURLSession *ephemeralSessionWiFiOnly = [NSURLSession sessionWithConfiguration:ephemeralConfiguration delegate:delegate delegateQueue:operationQueue];

用系統(tǒng)提供的代理獲取資源(block回調)

請求數(shù)據(jù)資源最直接的方法是使用系統(tǒng)提供的代理。用系統(tǒng)提供的方法,你需要在你的app中實現(xiàn)兩部分代碼:

  • 創(chuàng)建一個配置對象以及基于配置對象的會話
  • 實現(xiàn)一個回調,在這個回調中處理接收到的數(shù)據(jù)

用系統(tǒng)提供的代理方法,每個對應URL的請求,你只需要一段簡短的代碼,如 1-4:

提示:系統(tǒng)提供的代理方法對自定義網(wǎng)絡行為有限制。如果你的app有超出基本URL請求的特殊需求,如:自定義認證或者后臺下載,系統(tǒng)提供的代理并不是最優(yōu)選。要得到一個完整的列表,你需要實現(xiàn)一個完整的代理,你可以看看一個URL會話的生命周期。

NSURLSession *sessionWithoutADelegate = [NSURLSession sessionWithConfiguration:defaultConfiguration];
NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
 
[[sessionWithoutADelegate dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    NSLog(@"Got response %@ with error %@.\n", response, error);
    NSLog(@"DATA:\n%@\nEND DATA\n", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}] resume];

用自定義代理方法獲取數(shù)據(jù)

如果你在使用自定義代理去獲取數(shù)據(jù),你至少要實現(xiàn)以下兩個方法:

URLSession:dataTask:didReceiveData: method調用后,如果你的app需要使用返回的數(shù)據(jù),那么你需要利用代碼以某種方式存儲數(shù)據(jù)。
例如,一個web瀏覽器可能需要渲染它所已接收到的數(shù)據(jù)。需要這樣做,它可能需要用一個字典用任務對象task和一個可變對象做映射,這個可變數(shù)據(jù)對象NSMutableData是用來存儲接收到的數(shù)據(jù)的,它可以通過appendData:拼接接收到的新數(shù)據(jù)。
表1-5展示了如果創(chuàng)建并開啟一個數(shù)據(jù)任務

NSURL *url = [NSURL URLWithString: @"https://www.example.com/"];
NSURLSessionDataTask *dataTask = [defaultSession dataTaskWithURL:url];
[dataTask resume];

下載文件

在較高的層面上,下載一個文件file類似于獲取數(shù)據(jù),你的app應該實現(xiàn)下列代理方法:

重要提示:在以上方法return之前(我理解為代碼塊執(zhí)行完畢之前),你要么必須打開文件讀取數(shù)據(jù),要么把它移到一個永久性文件夾中。當這個方法return時,如果它仍舊存儲在原來的位置,你要把臨時文件夾刪除掉。

  •   [URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:](https://developer.apple.com/reference/foundation/nsurlsessiondownloaddelegate/1409408-urlsession?language=objc) 為你的應用提供了下載進度的狀態(tài)信息。
    
  • URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes: 告知你的應用它試圖開啟以前的失敗下載成功了(也就是以前下載失敗了,本次又開啟了下載狀態(tài))。

  • URLSession:task:didCompleteWithError: 告知你的應用下載失敗了。

如果你計劃在后臺會話中下載,那么當你的應用停止運行后下載依然在執(zhí)行。如果你話在默認會話或者臨時會話中下載,那么當你的應用重新運行時你必須重啟一個新的下載任務。
在和服務器交互(數(shù)據(jù)傳輸)過程中,如果用戶想要暫停下載,你可以調用cancelByProducingResumeData:方法。之后(恢復下載后),你的應用可以傳遞恢復后的返回數(shù)據(jù)給downloadTaskWithResumeData:downloadTaskWithResumeData:completionHandler:方法,創(chuàng)建新的下載任務繼續(xù)下載。

如果數(shù)據(jù)傳輸失敗,你的代理URLSession:task:didCompleteWithError: 方法將會被調用,返回NSError對象。如果任務task是可恢復的,對象的用戶信息字典中包含一個以NSURLSessionDownloadTaskResumeDatakey所對應的值,你的應用可以傳遞恢復后的返回數(shù)據(jù)給downloadTaskWithResumeData:downloadTaskWithResumeData:completionHandler:方法,創(chuàng)建新的下載任務繼續(xù)下載。
表1-6提供了一個下載一個比較大的文件的示例
表1-7提供了一個下載任務代理方法的例子

表1-6 下載任務事例

NSURL *url = [NSURL URLWithString:@"https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/ObjC_classic/FoundationObjC.pdf"];
NSURLSessionDownloadTask *downloadTask = [backgroundSession downloadTaskWithURL:url];
[downloadTask resume];

表1-7 下載任務的代理方法

- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    NSLog(@"Session %@ download task %@ wrote an additional %lld bytes (total %lld bytes) out of an expected %lld bytes.\n", session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
 
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
    NSLog(@"Session %@ download task %@ resumed at offset %lld bytes out of an expected %lld bytes.\n", session, downloadTask, fileOffset, expectedTotalBytes);
}
 
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    NSLog(@"Session %@ download task %@ finished downloading to URL %@\n", session, downloadTask, location);
 
    // Perform the completion handler for the current session
    self.completionHandlers[session.configuration.identifier]();
 
   // Open the downloaded file for reading
    NSError *readError = nil;
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingFromURL:location error:readError];
    // ...
 
   // Move the file to a new URL
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSURL *cacheDirectory = [[fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] firstObject];
    NSError *moveError = nil;
    if ([fileManager moveItemAtURL:location toURL:cacheDirectory error:moveError]) {
        // ...
    }
}

上傳正文內容(Uploading Body Content)

你的app(應用 )有3種方式上傳HTTPPOST請求的請求體內容,分別是以NSData、file(文件)以及stream(流)方式上傳。一般來說,你的app應該這樣做:

  • 如果你應用的內存中已經存有data,并且沒有什么理由去處理它,那么你就可以以NSData形式上傳。
  • 如你你要上傳的內容是以file的形式存儲在disk(硬盤)上,如果你正在進行后臺傳輸或者是如果為了釋放內存中相關的data而將數(shù)據(jù)寫到disk,這樣做對你的app更有益的話,那么此時你可以以file形式上傳。
  • 如果你是通過網(wǎng)絡接收數(shù)據(jù)的,你可以使用stream

無論你選擇哪種方式,只要你使用自定義代理方法,那么你就必須實現(xiàn)URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:方法以獲取上傳進度信息。

此外,如果你的app以stream的形式上傳請求體(request body),那么你必須實現(xiàn)一個自定義的回話代理實現(xiàn)URLSession:task:needNewBodyStream:方法,更詳細的信息可以看下面的 “以stream的方式上傳正文內容”

1. 以NSData的形式上傳正文內容(Uploading Body Content Using an NSData Object)

NSData的形式上傳正文內容,你的app需要調用uploadTaskWithRequest:fromData:completionHandler: 方法或者uploadTaskWithRequest:fromData:completionHandler: 方法,請求體數(shù)據(jù)(request body data)通過參數(shù)fromData傳入。
會話對象根據(jù)數(shù)據(jù)的長度,計算HTTP頭部信息的Content-Length。你的app要額外提供服務器可能需要的頭部信息,如內容類型(content type)作為URL請求對象的一部分。

表1-8 以data形式的上傳任務事例

NSURL *textFileURL = [NSURL fileURLWithPath:@"/path/to/file.txt"];
NSData *data = [NSData dataWithContentsOfURL:textFileURL];
 
NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:url];
mutableRequest.HTTPMethod = @"POST";
[mutableRequest setValue:[NSString stringWithFormat:@"%lld", data.length] forHTTPHeaderField:@"Content-Length"];
[mutableRequest setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
 
NSURLSessionUploadTask *uploadTask = [defaultSession uploadTaskWithRequest:mutableRequest fromData:data];
[uploadTask resume];

2. 以file的形式上傳正文內容(Uploading Body Content Using a File)

file的形式上傳正文內容(body content ),你的app需要調用uploadTaskWithRequest:fromFile:uploadTaskWithRequest:fromFile:completionHandler: 方法創(chuàng)建一個上傳任務,你需要提供一個文件路徑(file URL)讓任務讀取body content
會話對象根據(jù)數(shù)據(jù)的長度,計算HTTP頭部信息的Content-Length
如果你的app沒有為請求頭提供Content-Type,那么這個回話會自動提供一個。你的app可能要額外提供服務器需要的頭部信息,作為URL請求對象的一部分。

表1-9以file形式的上傳任務事例(蘋果官網(wǎng)這里寫錯了)

NSURL *textFileURL = [NSURL fileURLWithPath:@"/path/to/file.txt"];
 
NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:url];
mutableRequest.HTTPMethod = @"POST";
 
NSURLSessionUploadTask *uploadTask = [defaultSession uploadTaskWithRequest:mutableRequest fromFile:textFileURL];
[uploadTask resume];

3. 以Stream的形式上傳正文內容(Uploading Body Content Using a Stream)

stream的形式上傳正文內容,你的app需要調用uploadTaskWithStreamedRequest: 去創(chuàng)建上傳任務。上傳任務將讀取正文內容轉化為stream,你的app會提供一個含有該stream的請求對象。

你的app需要為請求頭提供額外的服務器可能需要的信息,如Content-TypeContent-Length作為URL請求對象的一部分。

此外,由于會話不能倒回去重讀以前的stream數(shù)據(jù),因此在你的app重新嘗試同一個請求時(之前請求過),你需要提供一個新的stream(例如,認證失敗時)。可以這樣做,你的app需要提供一個 URLSession:task:needNewBodyStream:方法,當這個方法被調用后,無論你的app需要執(zhí)行什么操作獲取或創(chuàng)建一個新的body stream,這個方法的block回調都會返回一個新的stream(這段翻譯的感覺不是特別通順,大家可以看官網(wǎng),也可以指出我翻譯錯的地方~~)。

提示: 如果要為文本內容提供stream傳輸,你的應用必須要實現(xiàn)URLSession:task:needNewBodyStream:代理方法,而這個方法和系統(tǒng)的代理方法是不相容的,也就是說在實現(xiàn)這個方法時不能同時實現(xiàn)系統(tǒng)自帶的代理方法。

表1-10以stream的形式上傳正文內容事例

NSURL *textFileURL = [NSURL fileURLWithPath:@"/path/to/file.txt"];
 
NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
NSMutableURLRequest *mutableRequest = [NSMutableURLRequest requestWithURL:url];
mutableRequest.HTTPMethod = @"POST";
mutableRequest.HTTPBodyStream = [NSInputStream inputStreamWithFileAtPath:textFileURL.path];
[mutableRequest setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
[mutableRequest setValue:[NSString stringWithFormat:@"%lld", data.length] forHTTPHeaderField:@"Content-Length"];
 
NSURLSessionUploadTask *uploadTask = [defaultSession uploadTaskWithStreamedRequest:mutableRequest];
[uploadTask resume];

4. 用下載任務上傳file

如果使用下載任務上傳body content ,那么當創(chuàng)建下載請求時,你的應用只能使用NSDatabody stream作為NSURLRequest object的一部分。
如果你用stream作為傳輸?shù)臄?shù)據(jù),你的app需要在認證失敗時提供URLSession:task:needNewBodyStream:代理方法以提供新的body stream,這個方法詳細闡述在上述3中。
除了數(shù)據(jù)的返回方式,數(shù)據(jù)下載任務(download task)的行為很像一個數(shù)據(jù)任務(data task

note:Kerberos authentication is handled transparently.(直面翻譯是Kerberos身份驗證是透明處理的,但是不是特別懂,誰懂的可以告知一下)

當一個基于stream傳輸?shù)纳蟼魅蝿盏恼J證失敗時,上傳任務就不能重復利用這個stream了(前面說了)。相反,NSURLSession對象代理的URLSession:task:needNewBodyStream: 代理方法獲取新的NSInputStream(輸入流)對象為新的請求提供body data(如果上傳任務的 body(請求體)是由fileNSData對象提供的,那么會話對象將不進行這個調用)。

想更詳細了解關于寫一個NSURLSessionauthentication delegate(認證代理)方法,可以看Authentication Challenges and TLS Chain Validation

處理iOS的后臺活動(Handling iOS Background Activity)

如果你在在iOS中使用了NSURLSession,當一個下載完成時你的app自動重啟。你app的代理application:handleEventsForBackgroundURLSession:completionHandler: 要負責創(chuàng)建合適的session以及持有回調block,當session調用它的代理URLSessionDidFinishEventsForBackgroundURLSession: 方法時再調用這個存儲的回調block。

表1-11展示了一個創(chuàng)建和開啟后臺下載任務的事例

NSURL *url = [NSURL URLWithString:@"https://www.example.com/"];
 
NSURLSessionDownloadTask *backgroundDownloadTask = [backgroundSession downloadTaskWithURL:url];
[backgroundDownloadTask resume];

表1-12和1-13展示了分別展示了這些sessons和app delegate 方法

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
    AppDelegate *appDelegate = (AppDelegate *)[[[UIApplication sharedApplication] delegate];
    if (appDelegate.backgroundSessionCompletionHandler) {
        CompletionHandler completionHandler = appDelegate.backgroundSessionCompletionHandler;
        appDelegate.backgroundSessionCompletionHandler = nil;
        completionHandler();
    }
 
    NSLog(@"All tasks are finished");
}

表1-13 App后臺下載的代理方法

@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (copy) CompletionHandler backgroundSessionCompletionHandler;
 
@end
 
@implementation AppDelegate
 
- (void)application:(UIApplication *)application
handleEventsForBackgroundURLSession:(NSString *)identifier
  completionHandler:(void (^)())completionHandler
{
    self.backgroundSessionCompletionHandler = completionHandler;
}
 
@end

完結(這部分如果有翻譯錯誤的,各位如果有發(fā)現(xiàn)的希望給我指出哈,在此感謝了!!!)。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容