由於傲嬌的蘋果在 iOS9 之後已經放棄了 NSURLConnection,所以在現在的實際開發中,除了大家常見的 AFN 框架,一般使用的是 iOS7 之後推出的 NSURLSession,作為一名 iOS 開發人員,如果你只知道 AFN 框架來進行網路請求,那就只能說是 too young too simple,sometimes naive。
目錄
- NSURLSession 的優勢
- NSURLSessionTask 的子類
- NSURLSessionDataTask 傳送 GET 請求
- NSURLSessionDataTask 傳送 POST 請求
- NSURLSessionDataTask 設定代理髮送請求
- 設定代理之後的強引用問題
- NSURLSessionDataTask 簡單下載
- NSURLSessionDownloadTask 簡單下載
- dataTask 和 downloadTask 下載對比
- 寫在最後
- 【補充】NSURLSession 詳解離線斷點下載的實現
NSURLSession 的優勢
- NSURLSession 支援 http2.0 協議
- 在處理下載任務的時候可以直接把資料下載到磁碟
- 支援後臺下載|上傳
- 同一個 session 傳送多個請求,只需要建立一次連線(複用了TCP)
- 提供了全域性的 session 並且可以統一配置,使用更加方便
- 下載的時候是多執行緒非同步處理,效率更高
NSURLSessionTask 的子類
- NSURLSessionTask 是一個抽象類,如果要使用那麼只能使用它的子類
- NSURLSessionTask 有兩個子類
- NSURLSessionDataTask,可以用來處理一般的網路請求,如 GET | POST 請求等
- NSURLSessionDataTask 有一個子類為 NSURLSessionUploadTask,用於處理上傳請求的時候有優勢
- NSURLSessionDownloadTask,主要用於處理下載請求,有很大的優勢
- NSURLSessionDataTask,可以用來處理一般的網路請求,如 GET | POST 請求等
NSURLSessionDataTask 傳送 GET 請求
傳送 GET 請求的步驟非常簡單,只需要兩步就可以完成:
- 使用 NSURLSession 物件建立 Task
- 執行 Task
12345678910111213141516171819202122232425//確定請求路徑NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520&pwd=520&type=JSON"];//建立 NSURLSession 物件NSURLSession *session = [NSURLSession sharedSession];/**根據物件建立 Task 請求url 方法內部會自動將 URL 包裝成一個請求物件(預設是 GET 請求)completionHandler 完成之後的回撥(成功或失敗)param data 返回的資料(響應體)param response 響應頭param error 錯誤資訊*/NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {//解析伺服器返回的資料NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);//預設在子執行緒中解析資料NSLog(@"%@", [NSThread currentThread]);}];//傳送請求(執行Task)[dataTask resume];
NSURLSessionDataTask 傳送 POST 請求
傳送 POST 請求的步驟與傳送 GET 請求一樣:
- 使用 NSURLSession 物件建立 Task
- 執行 Task
12345678910111213141516171819//確定請求路徑NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];//建立可變請求物件NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:url];//修改請求方法requestM.HTTPMethod = @"POST";//設定請求體requestM.HTTPBody = [@"username=520&pwd=520&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];//建立會話物件NSURLSession *session = [NSURLSession sharedSession];//建立請求 TaskNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:requestM completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {//解析返回的資料NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);}];//傳送請求[dataTask resume];
NSURLSessionDataTask 設定代理髮送請求
- 建立 NSURLSession 物件設定代理
- 使用 NSURLSession 物件建立 Task
- 執行 Task
1234567891011121314151617181920//確定請求路徑NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];//建立可變請求物件NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:url];//設定請求方法requestM.HTTPMethod = @"POST";//設定請求體requestM.HTTPBody = [@"username=520&pwd=520&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];//建立會話物件,設定代理/**第一個引數:配置資訊第二個引數:設定代理第三個引數:佇列,如果該引數傳遞nil 那麼預設在子執行緒中執行*/NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]delegate:self delegateQueue:nil];//建立請求 TaskNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:requestM];//傳送請求[dataTask resume]; - 遵守協議,實現代理方法(常用的有三種代理方法)
1234567891011121314151617181920212223-(void)URLSession:(NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTaskdidReceiveResponse:(nonnull NSURLResponse *)responsecompletionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler {//子執行緒中執行NSLog(@"接收到伺服器響應的時候呼叫 -- %@", [NSThread currentThread]);self.dataM = [NSMutableData data];//預設情況下不接收資料//必須告訴系統是否接收伺服器返回的資料completionHandler(NSURLSessionResponseAllow);}-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {NSLog(@"接受到伺服器返回資料的時候呼叫,可能被呼叫多次");//拼接伺服器返回的資料[self.dataM appendData:data];}-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {NSLog(@"請求完成或者是失敗的時候呼叫");//解析伺服器返回資料NSLog(@"%@", [[NSString alloc] initWithData:self.dataM encoding:NSUTF8StringEncoding]);}
設定代理之後的強引用問題
- NSURLSession 物件在使用的時候,如果設定了代理,那麼 session 會對代理物件保持一個強引用,在合適的時候應該主動進行釋放
- 可以在控制器呼叫 viewDidDisappear 方法的時候來進行處理,可以通過呼叫 invalidateAndCancel 方法或者是 finishTasksAndInvalidate 方法來釋放對代理物件的強引用
- invalidateAndCancel 方法直接取消請求然後釋放代理物件
- finishTasksAndInvalidate 方法等請求完成之後釋放代理物件。
1[self.session finishTasksAndInvalidate];
NSURLSessionDataTask 簡單下載
在前面請求資料的時候就相當於一個簡單的下載過程,NSURLSessionDataTask 下載檔案具體的步驟與上類似:
- 使用 NSURLSession 物件建立一個 Task 請求
- 執行請求
123456789101112[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"]completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {//解析資料UIImage *image = [UIImage imageWithData:data];//回到主執行緒設定圖片dispatch_async(dispatch_get_main_queue(), ^{self.imageView.image = image;});}] resume];
NSURLSessionDownloadTask 簡單下載
- 使用 NSURLSession 物件建立下載請求
- 在下載請求中移動檔案到指定位置
- 執行請求
12345678910111213141516171819202122232425//確定請求路徑NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"];//建立請求物件NSURLRequest *request = [NSURLRequest requestWithURL:url];//建立會話物件NSURLSession *session = [NSURLSession sharedSession];//建立會話請求//優點:該方法內部已經完成了邊接收資料邊寫沙盒的操作,解決了記憶體飆升的問題NSURLSessionDownloadTask *downTask = [session downloadTaskWithRequest:requestcompletionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {//預設儲存到臨時資料夾 tmp 中,需要剪下檔案到 cacheNSLog(@"%@", location);//目標位置NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]stringByAppendingPathComponent:response.suggestedFilename];/**fileURLWithPath:有協議頭URLWithString:無協議頭*/[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];}];//傳送請求[downTask resume];
以上方法無法監聽下載進度,如要獲取下載進度,可以使用代理的方式進行下載。
dataTask 和 downloadTask 下載對比
- NSURLSessionDataTask
- 下載檔案可以實現離線斷點下載,但是程式碼相對複雜
- NSURLSessionDownloadTask
- 下載檔案可以實現斷點下載,但不能離線斷點下載
- 內部已經完成了邊接收資料邊寫入沙盒的操作
- 解決了下載大檔案時的記憶體飆升問題
寫在最後
關於使用 NSURLSession 進行上傳檔案操作,我只想說真的很麻煩,建議大家時間充沛且有興趣的可以研究一下,如果不想研究也是可以的,繼續使用我們偉大的 AFN 框架就好。至於 AFN 框架的使用,這裡就不贅述了,後期如果有時間會更新一些常用的 AFN 使用方法,敬請期待。
附:使用 NSURLSession 上傳檔案主要步驟及注意點
- 主要步驟:
- 確定上傳請求的路徑( NSURL )
- 建立可變的請求物件( NSMutableURLRequest )
- 修改請求方法為 POST
- 設定請求頭資訊(告知伺服器端這是一個檔案上傳請求)
- 按照固定的格式拼接要上傳的檔案等引數
- 根據請求物件建立會話物件( NSURLSession 物件)
- 根據 session 物件來建立一個 uploadTask 上傳請求任務
- 執行該上傳請求任務(呼叫 resume 方法)
- 得到伺服器返回的資料,解析資料(上傳成功 | 上傳失敗)
- 注意點:
- 建立可變的請求物件,因為需要修改請求方法為 POST,設定請求頭資訊
- 設定請求頭這個步驟可能會被遺漏
- 要處理上傳引數的時候,一定要按照固定的格式來進行拼接
- 需要採用合適的方法來獲得上傳檔案的二進位制資料型別( MIMEType,獲取方式如下)
- 點選這裡搜尋
- 對著該檔案傳送一個網路請求,接收到該請求響應的時候,可以通過響應頭資訊中的 MIMEType 屬性得到
- 使用通用的二進位制資料型別表示任意的二進位制資料 application/octet-stream
- 呼叫 C 語言的 API 來獲取
1[self mimeTypeForFileAtPath:@"此處為上傳檔案的路徑"]