ios - NSURLSession的使用

一. 背景

NSURLSession是蘋果在iOS7后推出的通過HTTP協議下載數據的API,該類提供了大量的代理方法,來支持認證和后臺下載功能。
使用NSURLSession須知:
a> 我們的應用會創建一系列的對話,每個對話負責一組的數據傳輸任務,并且會添加一系列的任務,每個任務代表著一個URL請求。
b> NSURLSession的API是異步的。
c> NSURLSession的使用分成:1.系統代理的方式(需要提供一個請求完成后處理的block)2. 自定義代理的方式(執行代理方法來處理請求成功或失敗)
d> NSURLSession支持取消、恢復、掛起操作,以及斷掉續傳的功能

二. NSURLSession

NSURLSession根據配置對象(NSURLSessionConfiguration)分為三大類型
  1. 默認會話(defaultSessionConfiguration):存儲cookie(緩存)在磁盤中,存儲證書在用戶keychain
  2. 后臺會話(backgroundSessionConfiguration):存儲cookie(緩存)在磁盤中,存儲證書在用戶keychain。后臺會添加獨立的進程來處理數據傳輸任務
  3. 短暫會話(ephemeralSessionConfiguration):緩存和證書都存在RAM中,會話結束,它們就自動釋放
任務類型也分為三大類
  1. dataTask:使用NSData對象進行上傳和下載數據;數據可一次性返回,也可以分片段返回;返回的數據不是進行文件存儲,所以不支持后臺會話(iOS8后支持 備注:主要用于一些小數據請求,或大文件的斷點續傳下載
  2. downloadTask:以文件的形式接收數據,當程序不運行時支持后臺下載
  3. uploadTask:以文件的形式上傳數據,支持后臺下載
簡單使用
1. 系統代理的方式

a>. dataTask

 //  1.創建NSURLSession單例對象
    NSURLSession *session = [NSURLSession sharedSession];
    
    //  2.添加dataTask任務(1>. 通過NSURLRequest方式 ;2>. 通過NSURL方式(內部進行封裝成NSURLRequest對象))
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx"]];
    /*
     POST請求請加這兩句
     request.HTTPMethod = @"POST";
     request.HTTPBody = [@"參數" dataUsingEncoding:NSUTF8StringEncoding];
     */
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    /*
     data:請求成功后返回的數據
     response:請求頭
     error:如果有值說明請求失敗
     */
    }];
    //或則
    //    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:@"http://xxx"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    //
    //    }];
    
    //  3.開始任務
    [dataTask resume];

b>. downLoadTask
內部已經實現邊下載邊寫入temp文件中,由于temp中的文件易清除,所以須手動將文件剪切到合適的沙河目錄 缺點:無法監控下載進度

    //  1.創建NSURLSession單例對象
    NSURLSession *session = [NSURLSession sharedSession];
    
    //  2.添加downloadTask任務(1>. 通過NSURLRequest方式 ;2>. 通過NSURL方式(內部進行封裝成NSURLRequest對象))
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx"]];
    /*
     POST請求請加這兩句
     request.HTTPMethod = @"POST";
     request.HTTPBody = [@"參數" dataUsingEncoding:NSUTF8StringEncoding];
     */
    NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        /*
         location:下載的文件的保存地址,默認是temp
         */
    }];
    //或則
//    NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:[NSURL URLWithString:@"http://xxx"] completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//        
//    }];

    //  3.開始任務
    [downloadTask resume];

c>. upLoadTask

    //  1.創建NSURLSession單例對象
    NSURLSession *session = [NSURLSession sharedSession];
    
    //  2.添加uploadTask任務
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx"]];
    NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:[NSURL URLWithString:@"daf.af.af.3422"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
    }];
    //  3.開始任務
    [uploadTask resume];
2.通過設置配置實現代理方法的方式

a>. dataTask下載文件

    //  1.創建默認配置的NSURLSession對象
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    /*
     NSURLSessionConfiguration:session對象的全局配置設置,一般使用默認配置就可以
     delegate:設置代理
     delegateQueue:代理方法在哪個隊列中執行(在哪個線程中調用),如果是主隊列那么在主線程中執行,如果是非主隊列,那么在子線程中執行
     */
    //  2.創建一個Task, 請求方法為get和post均可
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:@"http://xxxx"]];
    
    //  3.執行任務(可暫停、取消、恢復等)
    [dataTask resume];  // [dataTask suspend] [dataTask cancel]; 

//  4. 遵守代理協議,實現代理方法(3個相關的代理方法)

 /* 1.當接收到服務器響應的時候調用 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    
    //  通過該block回調,告訴服務器端是否接收返回的數據
    completionHandler(NSURLSessionResponseAllow);
    /*
     NSURLSessionResponseCancel = 0, 取消任務
     NSURLSessionResponseAllow = 1,  接收任務
     NSURLSessionResponseBecomeDownload = 2, 轉變成下載
     NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) = 3, 轉變成流
     */
}

/* 2.當接收到服務器返回的數據時調用 該方法可能會被調用多次 */
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {

}

/* 3.當請求完成之后調用該方法 不論是請求成功還是請求失敗都調用該方法,如果請求失敗,那么error對象有值,否則那么error對象為空 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    
}

b>. downloadTask下載文件

和dataTask一樣只是代理方法有所區別

//  4. 遵守代理協議,實現代理方法(3個相關的代理方法)

 /* 1.可以在該方法中監聽文件下載的進度,該方法會被調用多次
 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {  
 
}

/* 2.恢復下載的時候調用該方法 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {

}

/* 3.下載完成之后調用該方法 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(nonnull NSURL *)location {
    
}
/* 4.請求完成之后調用如果請求失敗,那么error有值 */
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error {
    
}

注意:

  1. 如果通過[downloadTask cancel]取消任務,那么任務不能恢復了
  2. 通過以下方法暫停,那么該方法會以resumeData保存當前文件的已經下載數據,任務可以恢復下載
[self.downloadTask cancelByProducingResumeData:^(NSData * __nullable resumeData) {
    self.resumeData = resumeData;
}];

恢復下載方法

self.downloadTask=[self.session downloadTaskWithResumeData:self.resumeData];
 [self.downloadTask resume];
  1. 缺點
    01 如果用戶點擊暫停之后退出程序,那么需要把恢復下載的數據寫一份到沙盒,代碼復雜度更
    02 如果用戶在下載中途未保存恢復下載數據即退出程序,則不具備可操作性

c>. uploadTask上傳文件

    //1.創建session
    self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    
    //2.創建task
    //2.1 創建請求對象
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxxxx"]];
    //2.2 設置請求方法
    request.HTTPMethod = @"POST";
    
    //2.3.設置請求頭
    NSString *header = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",str];
    [request setValue:header forHTTPHeaderField:@"Content-Type"];
    //2.4設置文件上傳的文件內容
    NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromData:data];
     [uploadTask resume];

// 4. 遵守代理協議,實現代理方法

-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
}

-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
}
后臺會話

當我們的運用退出,如果后臺下載任務完成或則需要證書時,后臺自動重啟我們的運用,同時調用UIApplicationDelegate對象application:handlerEventsForBackgroundURLSession:completionHandler:方法,這個方法會提供session的標識,然后我們要利用session的標識創建后臺配置,繼而創建新的后臺會話,與后臺的activity關聯。當后臺下載任務完成時,會調用后臺session的代理方法URLSessioinDidFinishEventsForBackgroundURLSession:然后調用存儲的完成處理

如果在程序掛起時有任何任務完成,則會調用URLSession:downloadTask:didFinishDownloadingToURL:方法。同樣的,如果任務需要證書,則NSURLSession對象會在適當的時候調用URLSession:task:didReceiveChallenge:completionHandler: 和URLSession:didReceiveChallenge:completionHandler:方法。

(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
{
    NSURLSessionConfiguration *backgroundConfigObject = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier];
    
    URLSession *sessionDelegate = [[URLSession alloc] init];
    
    NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfigObject
                                                                    delegate:sessionDelegate
                                                               delegateQueue:[NSOperationQueue mainQueue]];
    
    [sessionDelegate addCompletionHandler:completionHandler forSession:identifier];
}
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
    NSLog(@"background url session %@", session);
    
    if (session.configuration.identifier)
    {
        [self callCompletionHandlerForSession:session.configuration.identifier];
    }
}

- (void)callCompletionHandlerForSession:(NSString *)identifier
{
    CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey:identifier];
    
    if (handler) {
        [self.completionHandlerDictionary removeObjectForKey:identifier];
        
        handler();
    }
}
注意:

NSURLSession對象的釋放

 -(void)dealloc { 
//在最后的時候應該把session釋放,以免造成內存泄露 
// NSURLSession設置過代理后,需要在最后(比如控制器銷毀的時候)調用session的invalidateAndCancel或者resetWithCompletionHandler,才不會有內存泄露 
// [self.session invalidateAndCancel]; 
[self.session resetWithCompletionHandler:^{

    NSLog(@"釋放---");
}];
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容