你好,歡迎來到IOS教程網

 Ios教程網 >> IOS使用技巧 >> IOS技巧綜合 >> iOS —— NNSURLSessionDataTask

iOS —— NNSURLSessionDataTask

編輯:IOS技巧綜合
[摘要]本文是對iOS —— NNSURLSessionDataTask的講解,對學習IOS蘋果軟件開發有所幫助,與大家分享。

一、基本簡介

1. NSURLSessionDataTask 是 NSURLSessionTask 的子類,是一個具體的 網絡請求(task) 類,是網絡請求中最常用的請求之一

通常,NSURLSessionDataTask 用來請求數據,可以用來下載數據資源,例如 JSON 數據,圖片數據等

2. 通常有以下幾種方法創建一個 data task

1)方法一 : 使用 NSURLSession 的實例方法

1 // @param url 待請求的 URL 地址
2 // @param completionHandler 回調方法
3 // @param data 從服務器請求到的數據
4 // @param response 響應頭信息
5 // @param error 錯誤信息
6 - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;

注意 :

該方法中會自動將 url 轉換為一個請求對象(NSURLRequest),並且該請求對象是 GET 請求方式

回調方法是在子線程中運行的,所以如果在回調方法中刷新 UI 必須回到主線程中

回調方法中有兩個參數 response / error,這兩個參數和 該消息的接受者(即 NSURLSessionDataTask 對象)中的 response / error 是指同一個內容,即地址相同

使用該方法的缺點是不能監聽獲取數據的進度,因為只有當全部請求完數據後,才會調用這個方法,也就是說,data 中的數據是請求的全部數據

2)方法二 : 使用 NSURLSession 的實例方法

// @param request 請求對象
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;

方法二與方法一不同的地方在於 : 方法二可以手動設置請求對象,這樣一來,就可以指定請求方式 GET/POST 中的一個;而方法一只能是 GET 請求方式

剩余的全部一樣

3)方法三 : 代理

方法一和方法二唯一的缺點就是不能監控請求進度,因為只有當請求完全部的數據後才會調用回調方法,如果想要監控請求進度,必須使用代理的方法

使用代理首先要自定義 NSURLSession 對象,使用下面的方法可以設置代理對象

// @param configuration 配置信息對象
// @param delegate 代理對象
// @param queue 代理方法在哪個線程中運行,如果傳 nil 則會在子線程中運行代理方法
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;

同時,必須遵守相關的協議

在使用下面的方法創建 data task

- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;

這兩個方法的區別和方法一/方法二一樣,使用 url 則方法內部會自動將其轉換為一個 請求對象,並且是 GET 請求方式

二、示例代碼

因為我是把所有的代碼寫到一個 demo 裡的,所以有些目前不需要的東西可以不必理會

1)GET/POST 請求

首先在 main.storyboard 中拖入一個 UINavigationController ,並設置 staticcell,如圖

然後拖入一個 UIViewController 並選中第一個 cell,即 NSURLSessionDataTask,將 cell 和 UIViewController 連線,選擇 push

在這個 UIViewController 中拖入一系列控件,如圖

右上角的 UIBarButtonItem 是跳轉到下一個界面的,不用管它

UIViewController 中的代碼如下

 1 #import "LHDataTaskViewController.h"
 2 
 3 // GET 請求的 URL
 4 static NSString * imageURL = @"http://120.25.226.186:32812/resources/images/minion_01.png";
 5 
 6 // POST 請求的 URL
 7 static NSString * dataURL = @"http://api.hudong.com/iphonexml.do";
 8 
 9 @interface LHDataTaskViewController ()
10 
11 #pragma mark - 屬性
12 @property (weak, nonatomic) IBOutlet UIImageView *showImageView;
13 
14 @property (nonatomic, strong) NSURLSession * session;
15 @property (nonatomic, strong) NSURLSessionDataTask * dataTask;
16 
17 @end
18 
19 @implementation LHDataTaskViewController
20 
21 #pragma mark - ViewController 生命周期
22 - (void)viewDidLoad {
23     
24     [super viewDidLoad];
25     
26     // 1. 初始化 NSURLSession 對象
27     self.session = [NSURLSession sharedSession];
28 
29 }
30 
31 - (void)didReceiveMemoryWarning {
32     
33     [super didReceiveMemoryWarning];
34 
35 }

1. GET 請求

設置 "GET請求" 按鈕的 動作方法

 1 #pragma mark - button 動作方法
 2 #pragma mark 發送 GET 請求獲取圖片資源
 3 - (IBAction)GETButtonClick:(id)sender {
 4     
 5     NSLog(@"dataTask的狀態 --- %li", _dataTask.state);
 6     
 7     // 1. 初始化 NSURLSesionDataTask 對象
 8     self.dataTask = [_session dataTaskWithURL:[NSURL URLWithString:imageURL] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
 9                 
10         // 1). 定義 UIImage 對象,並用接受的數據(data)初始化
11         UIImage * image = [UIImage imageWithData:data];
12         
13         // 2). 返回主線程刷新UI
14         dispatch_async(dispatch_get_main_queue(), ^{
15             
16             self.showImageView.image = image;
17             
18         });
19         
20         NSLog(@"dataTask的狀態 --- %li", _dataTask.state);
21         
22         // 此時,所有數據已經全部接受完畢,所以,已經接受的的數據和所要接受的總數據是相等的
23         // 因為沒有發送數據,所以發送數據都為 0
24         NSLog(@"已接受到的數據量 --- %lli", _dataTask.countOfBytesReceived); // 48347
25         NSLog(@"所要接受到的數據總量 --- %lli", _dataTask.countOfBytesExpectedToReceive); // 48347
26         NSLog(@"已經發送的數據量 --- %lli", _dataTask.countOfBytesSent); // 0
27         NSLog(@"所要發送的數據總量 --- %lli", _dataTask.countOfBytesExpectedToSend); // 0
28         
29     }];
30     
31     // 2. 發送請求,執行 task
32     [_dataTask resume];
33     
34 }

其中,24行 —— 27行 中用到的屬性,在上一節已經介紹過

注意 : NSURLSessionTask 中所有的 task 都需要 resume 來開始

2. POST 請求

設置 "POST請求" 按鈕的動作方法

 1 - (IBAction)POSTButtonClick:(UIButton *)sender {
 2     
 3     // 1. 創建請求對象(可變)
 4     NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:dataURL]];
 5     
 6     // 2. 設置請求方法為 POST 請求
 7     request.HTTPMethod = @"POST";
 8     
 9     request.HTTPBody = [@"type=focus-c" dataUsingEncoding:NSUTF8StringEncoding];
10     
11     // 1. 初始化 NSURLSessionDataTask 對象
12     self.dataTask = [_session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
13         
14         NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
15         
16     }];
17     
18     [_dataTask resume];
19     
20 }

2. 代理

使用代理的方法來進行網絡請求,並且可以監控請求進度

現在 mian.storyboard 中拖入一個 UIViewController 並添加控件,如圖

本次請求的是一張圖片數據,請求完之後會將圖片顯示到屏幕上的 UIImageView,resume 按鈕是開始請求,pause 按鈕是暫停請求,cance 按鈕是取消請求,中間還有一個 UIProgressView,用於顯示請求的進度,並將這些控件與插座變量關聯

UIViewController 中的代碼如下,該 ViewController 要遵守相關協議 <NSURLSessionDataDelegate>

 1 #import "LHDataTaskDownloadViewController.h"
 2 
 3 // 待訪問的 URL
 4 static NSString * imageURL = @"http://f12.topit.me/o129/10129120625790e866.jpg";
 5 
 6 @interface LHDataTaskDownloadViewController () <NSURLSessionDataDelegate>
 7 
 8 #pragma mark - 屬性列表
 9 #pragma mark 插座變量
10 @property (weak, nonatomic) IBOutlet UIImageView *showImageView;
11 @property (weak, nonatomic) IBOutlet UIProgressView *progressView;
12 @property (weak, nonatomic) IBOutlet UIButton *resumeButton;
13 @property (weak, nonatomic) IBOutlet UIButton *pauseButton;
14 @property (weak, nonatomic) IBOutlet UIButton *cancelButton;
15 
16 #pragma mark 網絡對象
17 @property (nonatomic, strong) NSURLSession * session;
18 @property (nonatomic, strong) NSURLSessionDataTask * dataTask;
19 
20 #pragma mark 用於接受數據的對象
21 @property (nonatomic, strong) NSMutableData * data;
22 
23 @end
24 
25 @implementation LHDataTaskDownloadViewController
26 
27 - (void)viewDidLoad {
28     
29     [super viewDidLoad];
30     
31     // 1. 初始化 NSURLSession 對象,delegateQueue 為協議方法運行的線程,傳 nil 則在子線程中運行
32     self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
33     
34     // 2. 初始化 NSURLSessionDataTask 對象,默認為 GET
35     self.dataTask = [_session dataTaskWithURL:[NSURL URLWithString:imageURL]];
36     
37     // 2. 將 cancelButton 和 pauseButton 按鈕設置為不可用
38     _cancelButton.enabled = NO;
39     
40     _pauseButton.enabled = NO;
41 
42 }

開始按鈕的動作方法

 1 - (IBAction)resumeButtonClick:(id)sender {
 2     
 3     // 1. 判斷當前的狀態,data task 默認為 暫停狀態
 4     if (_dataTask.state == NSURLSessionTaskStateSuspended) {
 5         
 6         _pauseButton.enabled = YES;
 7         
 8         _cancelButton.enabled = YES;
 9     
10         // 1). 開始請求 task
11         [_dataTask resume];
12     
13     }
14     
15 }

暫停按鈕的動作方法

 1 - (IBAction)pauseButtonClick:(id)sender {
 2     
 3     // 1. 判斷 task 當前的狀態,如果處於正在接受數據的狀態,則暫停
 4     if (_dataTask.state == NSURLSessionTaskStateRunning) {
 5     
 6         [_dataTask suspend];
 7     
 8     }
 9     
10 }

取消按鈕的動作方法

 1 - (IBAction)cancelButtonClick:(id)sender {
 2     
 3     // 1. 判斷 task 當前的狀態,如果處於正在接受數據的狀態或暫停狀態,則取消
 4     if (_dataTask.state == NSURLSessionTaskStateRunning || _dataTask.state == NSURLSessionTaskStateSuspended) {
 5     
 6         // 1). 取消 task
 7         [_dataTask cancel];
 8         
 9         // 2). 設置滑動條的值
10         _progressView.progress = 0;
11         
12         // 3). 創建對話框VC
13         UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:@"提示" message:@"該 Task 已經被取消" preferredStyle:UIAlertControllerStyleAlert];
14         
15         // 4). 顯示對話框VC
16         [self presentViewController:alertVC animated:YES completion:nil];
17         
18         // 5). 創建動作按鈕
19         UIAlertAction * cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
20            
21             [self.navigationController popViewControllerAnimated:YES];
22             
23         }];
24         
25         // 6). 將動作按鈕添加到對話框VC
26         [alertVC addAction:cancelAction];
27     
28     }
29     
30 }

協議方法

#pragma mark 接收到服務器響應時調用,默認情況下不接受數據,所以要允許
// @param session 當前的會話對象
// @param dataTask 當前的 data task 對象
// @param response 響應頭對象
// @param completionHandler 回調
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    
    NSLog(@"%@", NSStringFromSelector(_cmd));

    // 1. 初始化數據對象
    self.data = [[NSMutableData alloc] init];
    
    // 2. 允許接受數據,如果沒有寫這句,則後面代理的方法不會被執行
    completionHandler(NSURLSessionResponseAllow);

}

其中,NSURLSessionResponseDisposition 是一個枚舉

1 typedef NS_ENUM(NSInteger, NSURLSessionResponseDisposition) {
2     NSURLSessionResponseCancel = 0,        // 取消接受數據,之後的代理方法不會被執行,相當於 [task cancel];          
3     NSURLSessionResponseAllow = 1,         // 允許接受數據,之後的代理方法會被執行
4     NSURLSessionResponseBecomeDownload = 2,// 使當前的 data task 變為 download task,當轉換為 download task 時,會將數據下載到 tmp 文件中,不需要再接受數據了,並且必須調用下面的 iii) 方法,並且在該方法中可以什麼都不寫,但必須被調用           
5     NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) = 3,// 使當前的 data task 變為 stream task
6 } NS_ENUM_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0);
 1 #pragma mark 接受到數據時調用,可能會調用多次
 2 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
 3     
 4     NSLog(@"%@", NSStringFromSelector(_cmd));
 5     
 6     // 1. 拼接收到的數據
 7     [self.data appendData:data];
 8     
 9     // 2. 回到主線程刷新 UI
10     dispatch_async(dispatch_get_main_queue(), ^{
11        
12         _progressView.progress = (float)_dataTask.countOfBytesReceived/_dataTask.countOfBytesExpectedToReceive;
13         
14     });
15     
16 }
 1 #pragma mark 請求結束或失敗時調用
 2 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
 3 
 4     NSLog(@"%@", NSStringFromSelector(_cmd));
 5     
 6     UIImage * image = [UIImage imageWithData:_data];
 7     
 8     dispatch_async(dispatch_get_main_queue(), ^{
 9         
10         NSLog(@"currentThread --- %@", [NSThread currentThread]);
11         
12         self.showImageView.image = image;
13         
14     });
15 
16 }

這個方法並不是 NSURLSessionTaskDelegate 協議中的方法,適合所有的 task

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved