NSURLSession學習整理

前言

蘋果在 iOS9 以後已經放棄了NSURLConnection,使用的是 iOS7 以後推出的 NSURLSession。php

NSURLSession 有以下優點:html

1.NSURLSession 支持 http2.0 協議json

2.在處理下載任務的時候,能夠直接把數據下載到磁盤(經過配置) 3.支持後臺下載|上傳(經過配置) 4.同一個 session 發送多個請求,只需創建一次鏈接(複用了TCP) 5.提供了全局的 session 而且能夠統一配置,使用更加方便 6.下載的時候是多線程異步處理,效率更高緩存

不少人都用過AFNetWorkingSDWebImage,其實底層就是封裝了NSURLSession來請求任務。如今學習一下。安全

AFNetWorking 能夠自動將服務端返回的 JSON 數據識別並解析出來,而使用 NSURLSession 則須要本身來完成。服務器

NSURLSession簡介

NSURLSession 提供了與各類協議(如HTTP、HTTPS),以及進行交互的API。NSURLSession 類對象(會話對象)就是用於管理這種交互過程。它是一個高度可配置的容器,經過使用其提供的API,可進行管理控制。markdown

簡單使用,實現基本的網絡請求。cookie

NSURL *url = [NSURL URLWithString:@"http://....."];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithURL:url  completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
   // handle  response
}];
[task resume];
複製代碼

全部的NSURLSession 網絡請求基本遵循如下幾個步驟:網絡

獲取NSURLSession對象,經過session對象建立 task 任務,執行 task(task 默認是掛起的,經過resume執行)session

NSURLSession工做模式

NSURLSession有3種工做模式,不一樣的工做模式解決不一樣的網絡請求場景。

  • 默認會話模式(default)

當你須要保存會話相關的caches、證書、cookies等是就使用default

  • 瞬時會話模式(ephemeral)

全部和會話相關的caches、證書、cookies等都被緩存在RAM中,當會話銷燬,緩存的數據會被自動清空。(可實現私密瀏覽)

  • 後臺會話模式(background)

須要在後臺進行上傳下載就用background 。它會返回一個後臺 session。可在應用程序掛起、退出或崩潰狀況下,運行上傳和下載任務。

上述工做模式由NSURLSessionConfiguration決定的。其三種建立方式以下:

+ (NSURLSessionConfiguration *)defaultSessionConfiguration;  
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;  
+ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier; 
複製代碼

NSURLSessionConfiguration提供了不少屬性,從 指定可用網絡,到 cookie,安全性,緩存策略,再到使用自定義協議,啓動事件的設置,以及用於移動設備優化的幾個新屬性,能夠找到幾乎任何你想要進行配置的選項。

關於緩存:

default模式,能夠將緩存存儲在磁盤上。路徑是沙盒路徑下Library/Caches/bundid/Cache.db。還有一個常見的設置就是cachePolicy(緩存策略),它決定要不要從緩存中獲取,好比request.cachePolicy = NSURLRequestUseProtocolCachePolicy

typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
    //對特定的 URL 請求使用網絡協議中實現的緩存邏輯。這是默認的策略。
    NSURLRequestUseProtocolCachePolicy = 0,

    //數據須要從原始地址加載。不使用現有緩存。
    NSURLRequestReloadIgnoringLocalCacheData = 1,

    // 不只忽略本地緩存,同時也忽略代理服務器或其餘中間介質目前已有的、協議容許的緩存。
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, 

    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,

    //不管緩存是否過時,先使用本地緩存數據。若是緩存中沒有請求所對應的數據,那麼從原始地址加載數據。
    NSURLRequestReturnCacheDataElseLoad = 2,

    //不管緩存是否過時,先使用本地緩存數據。若是緩存中沒有請求所對應的數據,那麼放棄從原始地址加載數據,請求視爲失敗(即:「離線」模式)。
    NSURLRequestReturnCacheDataDontLoad = 3,

    //從原始地址確認緩存數據的合法性後,緩存數據就可使用,不然從原始地址加載。
    NSURLRequestReloadRevalidatingCacheData = 5, 
};
複製代碼

NSURLSession建立

提供了3種建立方法:

一、使用共享的會話,該會話使用全局的Cache,Cookie和證書。

+ (NSURLSession *)sharedSession;  
複製代碼

二、經過NSURLSessionConfiguration配置來建立會話對象。

+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;  
複製代碼

三、經過設置NSURLSessionConfiguration配置、代理、隊列來建立會話對象。

+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue; 
複製代碼

NSURLSessionTask

使用NSURLSession的是爲了完成網絡請求任務。

任務由NSURLSessionTask 這個抽象類封裝,其子類封裝了程序三個最基本的網絡任務:獲取數據(如 JSON 或者 XML),上傳文件,下載文件。

NSURLSessionDataTask能夠用來處理通常的網絡請求如 GET 等。NSURLSessionUploadTask用於處理上傳請求。NSURLSessionDownloadTask主要用於處理下載請求。

下面詳細介紹下三種任務:

1. NSURLSessionDataTask

任務建立,可經過一個NSURLRequestNSURL建立。

//這兩個方法須要設置代理來接收數據
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;

//這兩個方法在completionHandler來接收數據
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
複製代碼

請求任務完成時,它會帶有相關聯的數據,可經過代理,或Block方式處理回調數據。

(1)代理方式

#import "ViewController.h"

@interface ViewController ()<NSURLSessionDelegate,NSURLSessionTaskDelegate>
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self sendRequest];
}
- (void)sendRequest{
    //建立請求
    NSURL *url = [NSURL URLWithString:@"http://httpbin.org/get"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    //設置request的緩存策略(決定該request是否要從緩存中獲取)
    request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
    
    //建立配置(決定要不要將數據和響應緩存在磁盤)
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    //configuration.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad;
    
    //建立會話
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
    //生成任務
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
    //建立的task是中止狀態,須要咱們去啓動
    [task resume];
}
//1.接收到服務器響應的時候調用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler{
    NSLog(@"接收響應");
    //必須告訴系統是否接收服務器返回的數據
    //默認是completionHandler(NSURLSessionResponseAllow)
    //能夠再這邊經過響應的statusCode來判斷否接收服務器返回的數據
    completionHandler(NSURLSessionResponseAllow);
}
//2.接受到服務器返回數據的時候調用,可能被調用屢次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
    NSLog(@"接收到數據");
    //通常在這邊進行數據的拼接,在方法3纔將完整數據回調
//    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
}
//3.請求完成或者是失敗的時候調用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error{
    NSLog(@"請求完成或者是失敗");
    //在這邊進行完整數據的解析,回調
}
//4.將要緩存響應的時候調用(必須是默認會話模式,GET請求才能夠)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
 willCacheResponse:(NSCachedURLResponse *)proposedResponse
 completionHandler:(void (^)(NSCachedURLResponse * _Nullable cachedResponse))completionHandler{
    //能夠在這邊更改是否緩存,默認的話是completionHandler(proposedResponse)
    //不想緩存的話能夠設置completionHandler(nil)
    completionHandler(proposedResponse);
}
@end
複製代碼

(2)Block方式(便捷、經常使用)

NSURL *url = [NSURL URLWithString:@"http://www.connect.com/login"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=Tom&pwd=123" dataUsingEncoding:NSUTF8StringEncoding];

//使用全局的會話
NSURLSession *session = [NSURLSession sharedSession];
// 經過request初始化task
NSURLSessionTask *task = [session dataTaskWithRequest:request
                                   completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
    NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
 }];
// 啓動
[task resume];
複製代碼

2. NSURLSessionUploadTask

繼承自NSURLSessionDataTask。UploadTask只不過在Http請求的時候,把數據放到Http Body中。 NSURLSessionUploadTask經過request建立,在上傳時指定文件源或數據源。

/=========代理方式===========/
//經過文件url來上傳
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;  
//經過文件data來上傳
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;  
//經過文件流來上傳
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;

/=========Block方式===========/
- (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種建立的區別:

NSData:若是對象已經在內存裏 File:若是對象在磁盤上,這樣作有助於下降內存使用 Stream:經過流對象,能夠不用一次性將全部的流數據加載到內存中 不過使用Stream必定要實現URLSession:task:needNewBodyStream:,由於Session沒辦法在從新嘗試發送Stream的時候找到數據源。

基本上傳任務的使用: Uploadtask 的建立須要使用一個 request,另外加上一個要上傳的 NSData 對象 或者是一個本地文件的路徑對應的 NSURL

NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSData *data = ...;

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request
                                                            fromData:data
                                                   completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
         // ...
     }];

[uploadTask resume];
複製代碼

上傳圖片的例子

- (void)uploadRequest{
    //建立請求
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.freeimagehosting.net/upload.php"]];
    //若是是上傳文字就是@"application/json"
    [request addValue:@"image/jpeg" forHTTPHeaderField:@"Content-Type"];
    [request addValue:@"text/html" forHTTPHeaderField:@"Accept"];
    [request setHTTPMethod:@"POST"];
    [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
    [request setTimeoutInterval:20];
    NSData * imagedata = UIImageJPEGRepresentation([UIImage imageNamed:@"person"],1.0);
    
    //建立配置(決定要不要將數據和響應緩存在磁盤)
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    //建立會話
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
    
    NSURLSessionUploadTask * uploadtask = [session uploadTaskWithRequest:request fromData:imagedata completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        //發送完成的回調
        
    }];
    [uploadtask resume];
}
//發送數據過程當中會執行(執行屢次)
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
    NSLog(@"發送數據中");
    //在這邊監聽發送的進度
    //progress = totalBytesSent/(float)totalBytesExpectedToSend
}
複製代碼

3. NSURLSessionDownloadTask

下載文件能夠實現斷點下載 內部已經實現了數據邊接收、邊寫入沙盒的操做(直接下載到磁盤) 支持BackgroundSession(後臺下載)

Download task 也須要一個 request或者Url,不一樣之處在於 completionHandler 這個 block。它是將數據一點點地寫入本地的臨時文件。因此在 completionHandler 裏,須要把文件從一個臨時地址,移動到一個永久的地址保存起來。而Datatask uploadtask 會在任務完成時一次性返回。

NSURL *URL = [NSURL URLWithString:@"http://example.com/file.zip"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request
                                                         completionHandler: ^(NSURL *location, NSURLResponse *response, NSError *error) {
        NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
        NSURL *documentsDirectoryURL = [NSURL fileURLWithPath:documentsPath];
        NSURL *newFileLocation = [documentsDirectoryURL URLByAppendingPathComponent:[[response URL] lastPathComponent]];
        [[NSFileManager defaultManager] copyItemAtURL:location toURL:newFileLocation error:nil];
    }];

[downloadTask resume];
複製代碼

普通下載過程: (代理方式,需遵照NSURLSessionDelegate,NSURLSessionDownloadDelegate

- (void)downloadRequest{
    //建立請求
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://httpbin.org/image/jpeg"]];
    
    //建立配置(決定要不要將數據和響應緩存在磁盤)
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    //建立會話
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
    
    NSURLSessionDownloadTask * downloadtask = [session downloadTaskWithRequest:request];
    [downloadtask resume];
}

//  代理回調方法
//1. downloadTask下載過程當中會執行
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    NSLog(@"下載中...");
    NSLog(@"寫入數據大小%lld,總寫入數據大小%lld,總指望數據大小%lld",bytesWritten,totalBytesWritten,totalBytesExpectedToWrite);
    //監聽下載的進度
}
//2.downloadTask下載完成的時候會執行
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
    NSLog(@"下載完成");
       //該方法內部已經完成了邊接收數據邊寫沙盒的操做,解決了內存飆升的問題
    //對數據進行使用,或者保存(默認存儲到臨時文件夾 tmp 中,須要剪切文件到 cache)
    
    //保存
    NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil];

    //使用
    NSData * data = [NSData dataWithContentsOfURL:location.filePathURL];
    UIImage * image = [UIImage imageWithData:data];
    UIImageWriteToSavedPhotosAlbum(image, nil,nil,nil);
}
//3.請求完成或者是失敗的時候調用(Session層次的Task完成的事件)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error{
    NSLog(@"請求完成或者是失敗");
}
複製代碼

接下來了解一下,下載的API。除了經過url或request下載外,還能夠經過以前已經下載的數據來建立下載任務(也就是斷點續傳)。一樣地能夠經過completionHandler指定任務完成後的回調代碼塊。

// 使用這種方式取消下載能夠獲得未來用來恢復的數據,保存起來
[self.task cancelByProducingResumeData:^(NSData *resumeData) {
    self.resumeData = resumeData;
}];

// 因爲下載失敗致使的下載中斷會進入此協議方法,也能夠獲得用來恢復的數據
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    // 保存恢復數據
    self.resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData];
}

// 恢復下載時接過保存的恢復數據
self.task = [self.session downloadTaskWithResumeData:self.resumeData];
// 啓動任務
[self.task resume];

複製代碼
  • 斷點續傳的前提是,服務器也支持斷點續傳。

相關文章
相關標籤/搜索