由于蘋果在 iOS9 之后已經放棄了 NSURLConnection,所以在現在的實際開發中,除了大家常見的 AFN 框架,一般使用的是 NSURLSession。
目錄
- 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 請求等
NSURLSession 的子類
NSURLSessionDataTask 發送 GET 請求
發送 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];
NSURLSessionDataTask 發送 POST 請求
發送 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];
NSURLSessionDataTask 設置代理發送請求
- 創建 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 簡單下載
在前面請求數據的時候就相當于一個簡單的下載過程,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];
NSURLSessionDownloadTask 簡單下載
- 使用 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];
以上方法無法監聽下載進度,如要獲取下載進度,可以使用代理的方式進行下載。
dataTask 和 downloadTask 下載對比
- NSURLSessionDataTask
- 下載文件可以實現離線斷點下載,但是代碼相對復雜
- NSURLSessionDownloadTask
- 下載文件可以實現斷點下載,但不能離線斷點下載
- 內部已經完成了邊接收數據邊寫入沙盒的操作
- 解決了下載大文件時的內存飆升問題
寫在最后
關于使用 NSURLSession 進行上傳文件操作,我只想說真的很麻煩,建議大家時間充沛且有興趣的可以研究一下,如果不想研究也是可以的,繼續使用我們偉大的 AFN 框架就好。至于 AFN 框架的使用,這里就不贅述了,后期如果有時間會更新一些常用的 AFN 使用方法,敬請期待。
附:使用 NSURLSession 上傳文件主要步驟及注意點
- 主要步驟:
- 確定上傳請求的路徑( NSURL )
- 創建可變的請求對象( NSMutableURLRequest )
- 修改請求方法為 POST
- 設置請求頭信息(告知服務器端這是一個文件上傳請求)
- 按照固定的格式拼接要上傳的文件等參數
- 根據請求對象創建會話對象( NSURLSession 對象)
- 根據 session 對象來創建一個 uploadTask 上傳請求任務
- 執行該上傳請求任務(調用 resume 方法)
- 得到服務器返回的數據,解析數據(上傳成功 | 上傳失敗)
- 注意點:
創建可變的請求對象,因為需要修改請求方法為 POST,設置請求頭信息
設置請求頭這個步驟可能會被遺漏
要處理上傳參數的時候,一定要按照固定的格式來進行拼接
-
需要采用合適的方法來獲得上傳文件的二進制數據類型( MIMEType,獲取方式如下)
- 點擊這里搜索
- 對著該文件發送一個網絡請求,接收到該請求響應的時候,可以通過響應頭信息中的 MIMEType 屬性得到
- 使用通用的二進制數據類型表示任意的二進制數據 application/octet-stream
- 調用 C 語言的 API 來獲取
[self mimeTypeForFileAtPath:@"此處為上傳文件的路徑"]