開發只懂 AFN ?搞定 NSURLSession 才是硬道理

由于蘋果在 iOS9 之后已經放棄了 NSURLConnection,所以在現在的實際開發中,除了大家常見的 AFN 框架,一般使用的是 NSURLSession。

目錄

  1. NSURLSession 的優勢
  2. NSURLSessionTask 的子類
  3. NSURLSessionDataTask 發送 GET 請求
  4. NSURLSessionDataTask 發送 POST 請求
  5. NSURLSessionDataTask 設置代理發送請求
  6. 設置代理之后的強引用問題
  7. NSURLSessionDataTask 簡單下載
  8. NSURLSessionDownloadTask 簡單下載
  9. dataTask 和 downloadTask 下載對比
  10. 寫在最后
  11. 【補充】NSURLSession 詳解離線斷點下載的實現

NSURLSession 的優勢

  • NSURLSession 支持 http2.0 協議
  • 在處理下載任務的時候可以直接把數據下載到磁盤
  • 支持后臺下載|上傳
  • 同一個 session 發送多個請求,只需要建立一次連接(復用了TCP)
  • 提供了全局的 session 并且可以統一配置,使用更加方便
  • 下載的時候是多線程異步處理,效率更高

NSURLSessionTask 的子類

  • NSURLSessionTask 是一個抽象類,如果要使用那么只能使用它的子類
  • NSURLSessionTask 有兩個子類
    • NSURLSessionDataTask,可以用來處理一般的網絡請求,如 GET | POST 請求等
      • NSURLSessionDataTask 有一個子類為 NSURLSessionUploadTask,用于處理上傳請求的時候有優勢
    • NSURLSessionDownloadTask,主要用于處理下載請求,有很大的優勢
NSURLSession 的子類

NSURLSessionDataTask 發送 GET 請求

發送 GET 請求的步驟非常簡單,只需要兩步就可以完成:

  1. 使用 NSURLSession 對象創建 Task
  2. 執行 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 請求一樣:

  1. 使用 NSURLSession 對象創建 Task
  2. 執行 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 設置代理發送請求

  1. 創建 NSURLSession 對象設置代理
  2. 使用 NSURLSession 對象創建 Task
  3. 執行 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];
  1. 遵守協議,實現代理方法(常用的有三種代理方法)
-(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 下載文件具體的步驟與上類似:

  1. 使用 NSURLSession 對象創建一個 Task 請求
  2. 執行請求
[[[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 簡單下載

  1. 使用 NSURLSession 對象創建下載請求
  2. 在下載請求中移動文件到指定位置
  3. 執行請求
//確定請求路徑
    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 上傳文件主要步驟及注意點

  • 主要步驟:
    1. 確定上傳請求的路徑( NSURL )
    2. 創建可變的請求對象( NSMutableURLRequest )
    3. 修改請求方法為 POST
    4. 設置請求頭信息(告知服務器端這是一個文件上傳請求)
    5. 按照固定的格式拼接要上傳的文件等參數
    6. 根據請求對象創建會話對象( NSURLSession 對象)
    7. 根據 session 對象來創建一個 uploadTask 上傳請求任務
    8. 執行該上傳請求任務(調用 resume 方法)
    9. 得到服務器返回的數據,解析數據(上傳成功 | 上傳失敗)
  • 注意點:
    1. 創建可變的請求對象,因為需要修改請求方法為 POST,設置請求頭信息

    2. 設置請求頭這個步驟可能會被遺漏

    3. 要處理上傳參數的時候,一定要按照固定的格式來進行拼接

    4. 需要采用合適的方法來獲得上傳文件的二進制數據類型( MIMEType,獲取方式如下)

      • 點擊這里搜索
      • 對著該文件發送一個網絡請求,接收到該請求響應的時候,可以通過響應頭信息中的 MIMEType 屬性得到
      • 使用通用的二進制數據類型表示任意的二進制數據 application/octet-stream
      • 調用 C 語言的 API 來獲取
      [self mimeTypeForFileAtPath:@"此處為上傳文件的路徑"]
      
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容