由於傲嬌的蘋果在 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 支持 http2.0 協議
在處理下載任務的時候可以直接把數據下載到磁盤
支持後台下載|上傳
同一個 session 發送多個請求,只需要建立一次連接(復用了TCP)
提供了全局的 session 並且可以統一配置,使用更加方便
下載的時候是多線程異步處理,效率更高
NSURLSessionTask 是一個抽象類,如果要使用那麼只能使用它的子類
NSURLSessionTask 有兩個子類
NSURLSessionDataTask,可以用來處理一般的網絡請求,如 GET | POST 請求等
NSURLSessionDataTask 有一個子類為 NSURLSessionUploadTask,用於處理上傳請求的時候有優勢
NSURLSessionDownloadTask,主要用於處理下載請求,有很大的優勢
發送 GET 請求的步驟非常簡單,只需要兩步就可以完成:
使用 NSURLSession 對象創建 Task
執行 Task
//確定請求路徑 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];
發送 POST 請求的步驟與發送 GET 請求一樣:
使用 NSURLSession 對象創建 Task
執行 Task
//確定請求路徑 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]; //創建請求 Task NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:requestM completionHandler: ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { //解析返回的數據 NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); }]; //發送請求 [dataTask resume];
創建 NSURLSession 對象設置代理
使用 NSURLSession 對象創建 Task
執行 Task
//確定請求路徑 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]; //創建請求 Task NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:requestM]; //發送請求 [dataTask resume];
遵守協議,實現代理方法(常用的有三種代理方法)
-(void)URLSession:(NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveResponse:(nonnull NSURLResponse *)response completionHandler:(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 方法等請求完成之後釋放代理對象。
[self.session finishTasksAndInvalidate];
在前面請求數據的時候就相當於一個簡單的下載過程,NSURLSessionDataTask 下載文件具體的步驟與上類似:
使用 NSURLSession 對象創建一個 Task 請求
執行請求
[[[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];
使用 NSURLSession 對象創建下載請求
在下載請求中移動文件到指定位置
執行請求
//確定請求路徑 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:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) { //默認存儲到臨時文件夾 tmp 中,需要剪切文件到 cache NSLog(@"%@", 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];
以上方法無法監聽下載進度,如要獲取下載進度,可以使用代理的方式進行下載。
NSURLSessionDataTask
下載文件可以實現離線斷點下載,但是代碼相對復雜
NSURLSessionDownloadTask
下載文件可以實現斷點下載,但不能離線斷點下載
內部已經完成了邊接收數據邊寫入沙盒的操作
解決了下載大文件時的內存飙升問題
關於使用 NSURLSession 進行上傳文件操作,我只想說真的很麻煩,建議大家時間充沛且有興趣的可以研究一下,如果不想研究也是可以的,繼續使用我們偉大的 AFN 框架就好。至於 AFN 框架的使用,這裡就不贅述了,後期如果有時間會更新一些常用的 AFN 使用方法,敬請期待。
附:使用 NSURLSession 上傳文件主要步驟及注意點
主要步驟:
確定上傳請求的路徑( NSURL )
創建可變的請求對象( NSMutableURLRequest )
修改請求方法為 POST
設置請求頭信息(告知服務器端這是一個文件上傳請求)
按照固定的格式拼接要上傳的文件等參數
根據請求對象創建會話對象( NSURLSession 對象)
根據 session 對象來創建一個 uploadTask 上傳請求任務
執行該上傳請求任務(調用 resume 方法)
得到服務器返回的數據,解析數據(上傳成功 | 上傳失敗)
注意點:
創建可變的請求對象,因為需要修改請求方法為 POST,設置請求頭信息
設置請求頭這個步驟可能會被遺漏
要處理上傳參數的時候,一定要按照固定的格式來進行拼接
需要采用合適的方法來獲得上傳文件的二進制數據類型( MIMEType,獲取方式如下)
點擊這裡搜索
對著該文件發送一個網絡請求,接收到該請求響應的時候,可以通過響應頭信息中的 MIMEType 屬性得到
使用通用的二進制數據類型表示任意的二進制數據 application/octet-stream
調用 C 語言的 API 來獲取
[self mimeTypeForFileAtPath:@"此處為上傳文件的路徑"]