NSURLSession學習筆記(一)簡介html
1、URL Session的基本概念ios
1.三種工做模式:git
默認會話模式(default):工做模式相似於原來的NSURLConnection,使用的是基於磁盤緩存的持久化策略,使用用戶keychain中保存的證書進行認證受權。github
瞬時會話模式(ephemeral):該模式不使用磁盤保存任何數據。全部和會話相關的caches,證書,cookies等都被保存在RAM中,所以當程序使會話無效,這些緩存的數據就會被自動清空。web
後臺會話模式(background):該模式在後臺完成上傳和下載,在建立Configuration對象的時候須要提供一個NSString類型的ID用於標識完成工做的後臺會話。xcode
2.NSURLSession支持的三種任務緩存
NSURLSession類支持三種類型的任務:加載數據,下載和上傳。性能優化
2、相關的類cookie
NSURLConnection這個名字,實際上指的是一組構成Foundation框架中URL加載系統的相互關聯的組件:NSURLRequest,NSURLResponse,NSURLProtocol,NSURLCache,NSHTTPCookieStorage,NSURLCredentialStorage,以及和它同名的NSURLConnection。網絡
在WWDC 2013中,Apple的團隊對NSURLConnection進行了重構,並推出了NSURLSession做爲替代。
NSURLSession也是一組相互依賴的類,它的大部分組件和NSURLConnection中的組件相同如NSURLRequest,NSURLCache等。而NSURLSession的不一樣之處在於,它將NSURLConnection替換爲NSURLSession和NSURLSessionConfiguration,以及3個NSURLSessionTask的子類:NSURLSessionDataTask, NSURLSessionUploadTask, 和NSURLSessionDownloadTask。
下面來講下NSURLSession新推出的類:
1.NSURLSessionConfiguration類
其中NSURLSessionConfiguration用於配置會話的屬性,能夠經過該類配置會話的工做模式:
- + (NSURLSessionConfiguration *)defaultSessionConfiguration;
- + (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
- + (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;
在backgroundSessionConfiguration:方法中的identifier參數指定了會話的ID,用於標記後臺的session。
該類的其中兩個屬性:
- @property BOOL allowsCellularAccess;
-
- @property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(NA, 7_0);
allowsCellularAccess 屬性指定是否容許使用蜂窩鏈接, discretionary屬性爲YES時表示當程序在後臺運做時由系統本身選擇最佳的網絡鏈接配置,該屬性能夠節省經過蜂窩鏈接的帶寬。在使用後臺傳輸數據的時候,建議使用discretionary屬性,而不是allowsCellularAccess屬性,由於它會把WiFi和電源可用性考慮在內。補充:這個標誌容許系統爲分配任務進行性能優化。這意味着只有當設備有足夠電量時,設備才經過Wifi進行數據傳輸。若是電量低,或者只僅有一個蜂窩鏈接,傳輸任務是不會運行的。後臺傳輸老是在discretionary模式下運行。
2.NSURLSession類
獲取NSURLSession類對象有幾種方式:
- + (NSURLSession *)sharedSession;
-
- + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
- + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;
第一種方式是使用靜態的sharedSession方法,該類使用共享的會話,該會話使用全局的Cache,Cookie和證書。
第二種方式是經過sessionWithConfiguration:方法建立對象,也就是建立對應配置的會話,與NSURLSessionConfiguration合做使用。
第三種方式是經過sessionWithConfiguration:delegate:delegateQueue方法建立對象,二三兩種方式能夠建立一個新會話並定製其會話類型。該方式中指定了session的委託和委託所處的隊列。當再也不須要鏈接時,能夠調用Session的invalidateAndCancel直接關閉,或者調用finishTasksAndInvalidate等待當前Task結束後關閉。這時Delegate會收到URLSession:didBecomeInvalidWithError:這個事件。Delegate收到這個事件以後會被解引用。
3.NSURLSessionTask類
NSURLSessionTask是一個抽象子類,它有三個子類:NSURLSessionDataTask,NSURLSessionUploadTask和NSURLSessionDownloadTask。這三個類封裝了現代應用程序的三個基本網絡任務:獲取數據,好比JSON或XML,以及上傳和下載文件。
下面是其繼承關係:
有多種方法建立對應的任務對象:
(1)NSURLSessionDataTask
經過request對象或url建立:
- - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
-
- - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;
經過request對象或url建立,同時指定任務完成後經過completionHandler指定回調的代碼塊:
- - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
- - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
(2)NSURLSessionUploadTask
經過request建立,在上傳時指定文件源或數據源。
- - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
-
- - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;
-
- - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;
在建立upload task對象時,經過completionHandler指定任務完成後的回調代碼塊:
- - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
- - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
(3)NSURLSessionDownloadTask
- - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;
-
- - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;
-
- - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
下載任務支持斷點續傳,第三種方式是經過以前已經下載的數據來建立下載任務。
一樣地能夠經過completionHandler指定任務完成後的回調代碼塊:
- - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;
- - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;
- - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;
4.NSURLSessionDelegate和NSURLSessionTaskDelegate協議
在協議的方法中能夠完成各類各樣的回調動做,如身份驗證、完成任務後的動做、錯誤處理和後臺任務完成的動做等。委託方法指定在NSURLSession中必定數量的字節傳輸使用int64_t類型的參數。
這裏只說下後臺任務的一個委託方法:
- - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session NS_AVAILABLE_IOS(7_0);
合做使用的ApplicationDelegate方法:
- - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler NS_AVAILABLE_IOS(7_0);
將任務切換到後臺以後,Session的Delegate不會再收到和Task相關的消息。當全部Task全都完成後,程序將被喚醒,並調用ApplicationDelegate的application:handleEventsForBackgroundURLSession:completionHandler:回調,在這裏要爲後臺session(由background session的identifier標識)指定對應的回調代碼塊。
隨後,對於每個完成的後臺Task調用該Session的Delegate中的URLSession:downloadTask:didFinishDownloadingToURL:(成功的話)和URLSession:task:didCompleteWithError:(成功或者失敗都會調用)方法作處理,以上的回調代碼塊能夠在這裏調用。
NSURLSession學習筆記(二)Session Task
Session Task分爲三種Data Task,Upload Task,Download Task。毫無疑問,Session Task是整個NSURLSession架構的核心目標。
下面寫了一個簡單的Demo來初步使用下三種任務對象。這裏使用的是convenience methods,並無定製session和使用協議,都是採用completionHandler做爲回調動做。
故事板內容爲:
第一種Data Task用於加載數據,使用全局的shared session和dataTaskWithRequest:completionHandler:方法建立。代碼以下:
- - (IBAction)loadData:(id)sender {
-
- [self.spinner startAnimating];
-
-
- NSURL *url = [NSURL URLWithString:@"http://blog.csdn.net/u010962810"];
- NSURLRequest *request = [NSURLRequest requestWithURL:url];
- NSURLSession *session = [NSURLSession sharedSession];
- NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
- completionHandler:
- ^(NSData *data, NSURLResponse *response, NSError *error) {
-
- [self showResponseCode:response];
-
-
- [self.webView loadData:data
- MIMEType:@"text/html"
- textEncodingName:@"utf-8"
- baseURL:nil];
-
-
- [self.spinner stopAnimating];
- }];
-
- [dataTask resume];
- }
-
- - (void)showResponseCode:(NSURLResponse *)response {
- NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
- NSInteger responseStatusCode = [httpResponse statusCode];
- NSLog(@"%d", responseStatusCode);
- }
completionHandler指定任務完成後的動做。注意必定要使用resume方法啓動任務。(Upload Task和Download Task同理)
運行結果:
第二種Upload Task用於完成上傳文件任務,使用方法相似:
- - (IBAction)uploadFile:(id)sender {
- }
第三種Download Task用於完成下載文件的任務,使用全局的shared session和downloadTaskWithRequest:completionHandler:方法建立。
注意:在下載任務完成後,下載的文件位於tmp目錄下,由代碼塊中的location指定(不妨輸出看看),咱們必需要在completion handler中將文件放到持久化的目錄下保存。代碼以下:
- - (IBAction)downloadFile:(id)sender {
- [self.spinner startAnimating];
-
- NSURL *URL = [NSURL URLWithString:@"http://b.hiphotos.baidu.com/image/w%3D2048/sign=6be5fc5f718da9774e2f812b8469f919/8b13632762d0f703b0faaab00afa513d2697c515.jpg"];
- NSURLRequest *request = [NSURLRequest requestWithURL:URL];
- NSURLSession *session = [NSURLSession sharedSession];
- NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request
- completionHandler:
- ^(NSURL *location, NSURLResponse *response, NSError *error) {
- [self showResponseCode:response];
-
-
- NSLog(@"%@", location);
-
-
- NSString *documentsPath = [self getDocumentsPath];
- NSURL *documentsDirectoryURL = [NSURL fileURLWithPath:documentsPath];
- NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:[[response URL] lastPathComponent]];
-
-
- NSFileManager *fileManager = [NSFileManager defaultManager];
- if ([fileManager fileExistsAtPath:[fileURL path] isDirectory:NULL]) {
- [fileManager removeItemAtURL:fileURL error:NULL];
- }
- [fileManager moveItemAtURL:location toURL:fileURL error:NULL];
-
-
- NSURLRequest *showImage_request = [NSURLRequest requestWithURL:fileURL];
- [self.webView loadRequest:showImage_request];
-
- [self.spinner stopAnimating];
- }];
-
- [downloadTask resume];
- }
-
- - (NSString *)getDocumentsPath {
- NSArray *documents = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
- NSString *documentsPath = documents[0];
- return documentsPath;
- }
運行結果:
這個Demo中沒有爲NSURLSession指定session的delegate,因此沒有使用委託中的方法,功能比較有限,並且也沒有自行定製session的配置,因此只能執行簡單的任務,可是對於加載數據,下載一張圖片等任務已經能夠應付自如。對於建立後臺下載任務,支持斷點續傳的下載任務等將在下一篇文章中分析介紹。
NSURLSession學習筆記(三)Download Task
NSURLSession的Download Task用於完成下載任務,本文介紹如何建立斷點續傳的下載任務和後臺下載任務。
咱們直接從分析Demo入手:
故事板以下:
只有一個View Controller,用於建立各類下載任務,並將下載後的圖片顯示到視圖上,下載過程當中會更新下載進度。
頭文件代碼以下:
- #import <UIKit/UIKit.h>
-
- @interface ViewController : UIViewController <NSURLSessionDownloadDelegate>
-
- @property (strong, nonatomic) NSURLSession *currentSession;
- @property (strong, nonatomic, readonly) NSURLSession *backgroundSession;
-
- @property (strong, nonatomic) NSURLSessionDownloadTask *cancellableTask;
- @property (strong, nonatomic) NSURLSessionDownloadTask *resumableTask;
- @property (strong, nonatomic) NSURLSessionDownloadTask *backgroundTask;
-
- @property (strong, nonatomic) NSData *partialData;
-
- @property (weak, nonatomic) IBOutlet UIImageView *downloadedImageView;
-
- @property (weak, nonatomic) IBOutlet UILabel *currentProgress_label;
- @property (weak, nonatomic) IBOutlet UIProgressView *downloadingProgressView;
-
- @property (weak, nonatomic) IBOutlet UIBarButtonItem *cancellableDownload_barButtonItem;
- @property (weak, nonatomic) IBOutlet UIBarButtonItem *resumableDownload_barButtonItem;
- @property (weak, nonatomic) IBOutlet UIBarButtonItem *backgroundDownload_barButtonItem;
- @property (weak, nonatomic) IBOutlet UIBarButtonItem *cancelTask_barButtonItem;
-
- - (IBAction)cancellableDownload:(id)sender;
- - (IBAction)resumableDownload:(id)sender;
- - (IBAction)backgroundDownload:(id)sender;
- - (IBAction)cancelDownloadTask:(id)sender;
-
- @end
1、建立普通的下載任務
這種下載任務是能夠取消的,代碼以下:
- - (IBAction)cancellableDownload:(id)sender {
- if (!self.cancellableTask) {
- if (!self.currentSession) {
- [self createCurrentSession];
- }
-
- NSString *imageURLStr = @"http://farm6.staticflickr.com/5505/9824098016_0e28a047c2_b_d.jpg";
- NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURLStr]];
- self.cancellableTask = [self.currentSession downloadTaskWithRequest:request];
-
- [self setDownloadButtonsWithEnabled:NO];
- self.downloadedImageView.image = nil;
-
- [self.cancellableTask resume];
- }
- }
若是當前的session爲空,首先須要建立一個session(該session使用默認配置模式,其delegate爲本身):
- - (void)createCurrentSession {
- NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
- self.currentSession = [NSURLSession sessionWithConfiguration:defaultConfig delegate:self delegateQueue:nil];
- self.currentSession.sessionDescription = kCurrentSession;
- }
隨後建立下載任務並啓動。
這種任務是可取消的,即下次下載又從0.0%開始:
- if (self.cancellableTask) {
- [self.cancellableTask cancel];
- self.cancellableTask = nil;
- }
2、建立可恢復的下載任務
可恢復的下載任務支持斷點續傳,也就是若是暫停當前任務,在下次再執行任務時,將從以前的下載進度中繼續進行。所以咱們首先須要一個NSData對象來保存已經下載的數據:
- @property (strong, nonatomic) NSData *partialData;
執行下載任務時,若是是恢復下載,那麼就使用downloadTaskWithResumeData:方法根據partialData繼續下載。代碼以下:
- - (IBAction)resumableDownload:(id)sender {
- if (!self.resumableTask) {
- if (!self.currentSession) {
- [self createCurrentSession];
- }
-
- if (self.partialData) {
- self.resumableTask = [self.currentSession downloadTaskWithResumeData:self.partialData];
- }
- else {
- NSString *imageURLStr = @"http://farm3.staticflickr.com/2846/9823925914_78cd653ac9_b_d.jpg";
- NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURLStr]];
- self.resumableTask = [self.currentSession downloadTaskWithRequest:request];
- }
-
- [self setDownloadButtonsWithEnabled:NO];
- self.downloadedImageView.image = nil;
-
- [self.resumableTask resume];
- }
- }
在取消下載任務時,要將partialData數據保存起來,並且不要調用cancel方法:
- else if (self.resumableTask) {
- [self.resumableTask cancelByProducingResumeData:^(NSData *resumeData) {
-
- self.partialData = resumeData;
- self.resumableTask = nil;
- }];
- }
另外在恢復下載時,NSURLSessionDownloadDelegate中的如下方法將被調用:
- - (void)URLSession:(NSURLSession *)session
- downloadTask:(NSURLSessionDownloadTask *)downloadTask
- didResumeAtOffset:(int64_t)fileOffset
- expectedTotalBytes:(int64_t)expectedTotalBytes {
- NSLog(@"NSURLSessionDownloadDelegate: Resume download at %lld", fileOffset);
- }
3、建立後臺下載任務
後臺下載任務,顧名思義,當程序進入後臺後,下載任務依然繼續執行。
首先建立一個後臺session單例,這裏的Session配置使用後臺配置模式,使用backgroundSessinConfiguration:方法配置時應該經過後面的參數爲該後臺進程指定一個標識符,在有多個後臺下載任務時這個標識符就起做用了。
- - (NSURLSession *)backgroundSession {
- static NSURLSession *backgroundSess = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:kBackgroundSessionID];
- backgroundSess = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
- backgroundSess.sessionDescription = kBackgroundSession;
- });
-
- return backgroundSess;
- }
在建立後臺下載任務時,應該使用後臺session建立,而後resume。
- - (IBAction)backgroundDownload:(id)sender {
- NSString *imageURLStr = @"http://farm3.staticflickr.com/2831/9823890176_82b4165653_b_d.jpg";
- NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURLStr]];
- self.backgroundTask = [self.backgroundSession downloadTaskWithRequest:request];
-
- [self setDownloadButtonsWithEnabled:NO];
- self.downloadedImageView.image = nil;
-
- [self.backgroundTask resume];
- }
在程序進入後臺後,若是下載任務完成,程序委託中的對應方法將被回調:
- - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
- NSLog(@"Application Delegate: Background download task finished");
-
-
- self.backgroundURLSessionCompletionHandler = completionHandler;
- }
而後調用NSURLSessionDownloadDelegate中的方法:
如下是
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL*)location中的方法,該方法只有下載成功才被調用:
- else if (session == self.backgroundSession) {
- self.backgroundTask = nil;
- AppDelegate *appDelegate = [AppDelegate sharedDelegate];
- if (appDelegate.backgroundURLSessionCompletionHandler) {
-
- void (^handler)() = appDelegate.backgroundURLSessionCompletionHandler;
- appDelegate.backgroundURLSessionCompletionHandler = nil;
- handler();
- }
- }
另外不管下載成功與否,如下方法都會被調用:
- - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
- NSLog(@"NSURLSessionDownloadDelegate: Complete task");
-
- dispatch_async(dispatch_get_main_queue(), ^{
- [self setDownloadButtonsWithEnabled:YES];
- });
-
- if (error) {
- NSLog(@"下載失敗:%@", error);
- [self setDownloadProgress:0.0];
- self.downloadedImageView.image = nil;
- }
- }
取消後臺下載任務時直接cancel便可:
- else if (self.backgroundTask) {
- [self.backgroundTask cancel];
- self.backgroundTask = nil;
- }
4、NSURLSessionDownloadDelegate
爲了實現下載進度的顯示,須要在委託中的如下方法中實現:
- - (void)URLSession:(NSURLSession *)session
- downloadTask:(NSURLSessionDownloadTask *)downloadTask
- didWriteData:(int64_t)bytesWritten
- totalBytesWritten:(int64_t)totalBytesWritten
- totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
- {
-
- double downloadProgress = totalBytesWritten / (double)totalBytesExpectedToWrite;
- [self setDownloadProgress:downloadProgress];
- }
-
- - (void)setDownloadProgress:(double)progress {
- NSString *progressStr = [NSString stringWithFormat:@"%.1f", progress * 100];
- progressStr = [progressStr stringByAppendingString:@"%"];
-
- dispatch_async(dispatch_get_main_queue(), ^{
- self.downloadingProgressView.progress = progress;
- self.currentProgress_label.text = progressStr;
- });
- }
從已經保存的數據中恢復下載任務的委託方法,fileOffset指定了恢復下載時的文件位移字節數:
- - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
- didResumeAtOffset:(int64_t)fileOffset
- expectedTotalBytes:(int64_t)expectedTotalBytes;
只有下載成功才調用的委託方法,在該方法中應該將下載成功後的文件移動到咱們想要的目標路徑:
- - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
- didFinishDownloadingToURL:(NSURL *)location;
不管下載成功或失敗都會調用的方法,相似於try-catch-finally中的finally語句塊的執行。若是下載成功,那麼error參數的值爲nil,不然下載失敗,能夠經過該參數查看出錯信息:
- - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
- didCompleteWithError:(NSError *)error;
後臺下載的運行結果:
啓動任務後,進入後臺:
下載完成後,控制檯將會「通知」咱們:
- 2014-02-05 18:30:39.767 DownloadTask[3472:70b] Application Delegate: App did become active
- 2014-02-05 18:30:43.734 DownloadTask[3472:70b] Application Delegate: App will resign active
- 2014-02-05 18:30:43.735 DownloadTask[3472:70b] Application Delegate: App did enter background
- 2014-02-05 18:30:45.282 DownloadTask[3472:70b] Application Delegate: Background download task finished
- 2014-02-05 18:30:45.285 DownloadTask[3472:4907] NSURLSessionDownloadDelegate: Finish downloading
- 2014-02-05 18:30:45.301 DownloadTask[3472:4907] NSURLSessionDownloadDelegate: Complete task
再次啓動程序,能夠看到加載好的頁面:
能夠看到,經過後臺下載讓咱們的程序更加異步地運行。NSURLSession封裝了對應的接口,讓咱們要執行的任務更加專門化,這個新的網絡架構的功能真的很強大。
本文的Demo基於https://github.com/ShinobiControls/iOS7-day-by-day改寫,內容基本一致。
原來的Demo也有一篇博客對應:iOS7 Day-by-Day :: Day 1 :: NSURLSession。
本文的Demo也已經上傳,有興趣的話能夠下載看看。