NSURLConnection 和 NSURLSession

簡介

NSURLConnection 是 2003 年 iOS 2.0 隨著第一版 Safari 的發(fā)布而發(fā)布的,它不單單是一個網(wǎng)絡(luò)請求類,而是指代 Foundation 框架的 URL 系統(tǒng)中的一系列關(guān)聯(lián)的組件: NSURLRequest哮洽、NSURLResponse双肤、NSURLProtocol纹磺、NSHTTPCookieStorage帖烘、NSURLCredentialStorage 以及同名類 NSURLConnection。

2013 年的 WWDC 大會上橄杨,iOS 7.0 推出了 NSURLSession蚓让,對 Foundation URL 加載系統(tǒng)進(jìn)行了徹底的重構(gòu),提供了更豐富的 API 來處理網(wǎng)絡(luò)請求讥珍,如:支持 http2.0 協(xié)議、直接把數(shù)據(jù)下載到磁盤窄瘟、同一 session 發(fā)送多個請求衷佃、下載是多線程異步處理和提供全局的 session 并可以統(tǒng)一配置等等,提高了 NSURLSession 的易用性蹄葱、靈活性氏义,更加地適合移動開發(fā)的需求。

區(qū)別

NSURLSession 是 NSURLConnection 的替代者图云,在 2013 年蘋果全球開發(fā)者大會上(WWDC2013)隨 iOS 7 一起發(fā)布的惯悠,是對 NSURLConnection 進(jìn)行了重構(gòu)優(yōu)化后的新的網(wǎng)絡(luò)接口。從 iOS 9 開始竣况,NSURLConnection 中發(fā)送請求的兩個方法已經(jīng)過期(同步請求克婶,異步請求),初始化網(wǎng)絡(luò)連接的方法也被設(shè)置為過期,系統(tǒng)不再推薦使用情萤,建議使用 NSURLSession 發(fā)送網(wǎng)絡(luò)請求鸭蛙。

普通任務(wù)和上傳

NSURLSession 針對下載/上傳等復(fù)雜的網(wǎng)絡(luò)操作提供了專門的解決方案,針對普通筋岛、上傳和下載分別對應(yīng)三種不同的網(wǎng)絡(luò)請求任務(wù):NSURLSessionDataTask娶视,NSURLSessionUploadTask 和 NSURLSessionDownloadTask。創(chuàng)建的 task 都是掛起狀態(tài)睁宰,需要 resume 才能啟動肪获。

當(dāng)服務(wù)器返回的數(shù)據(jù)較小時,NSURLSession 與 NSURLConnection 執(zhí)行普通任務(wù)的操作步驟沒有區(qū)別柒傻。
執(zhí)行上傳任務(wù)時孝赫,NSURLSession 與 NSURLConnection 一樣需要設(shè)置 POST 請求的請求體進(jìn)行上傳。

下載任務(wù)方式

NSURLConnection下載文件時诅愚,先是將整個文件下載到內(nèi)存寒锚,然后再寫入到沙盒,如果文件比較大违孝,就會出現(xiàn)內(nèi)存暴漲的情況刹前。

而使用 NSURLSessionDownloadTask 下載文件,會默認(rèn)下載到沙盒中的 tmp 文件中雌桑,不會出現(xiàn)內(nèi)存暴漲的情況喇喉,但是在下載完成后會把 tmp 中的臨時文件刪除,需要在初始化任務(wù)方法時校坑,在 completionHandler 回調(diào)中增加保存文件的代碼拣技。

請求方法的控制

NSURLConnection 實例化對象,實例化開始耍目,默認(rèn)請求就發(fā)送(同步發(fā)送)膏斤,不需要調(diào)用 start 方法。而 cancel 可以停止請求的發(fā)送邪驮,停止后不能繼續(xù)訪問莫辨,需要創(chuàng)建新的請求。

NSURLSession 有三個控制方法毅访,取消(cancel)沮榜、暫停(suspend)、繼續(xù)(resume)喻粹,暫停以后可以通過繼續(xù)恢復(fù)當(dāng)前的請求任務(wù)蟆融。

斷點續(xù)傳的方式

NSURLConnection 進(jìn)行斷點下載,通過設(shè)置訪問請求的 HTTPHeaderField 的 Range 屬性守呜,開啟運行循環(huán)型酥,NSURLConnection 的代理方法作為運行循環(huán)的事件源山憨,接收到下載數(shù)據(jù)時代理方法就會持續(xù)調(diào)用,并使用 NSOutputStream 管道流進(jìn)行數(shù)據(jù)保存冕末。

NSURLSession 進(jìn)行斷點下載萍歉,當(dāng)暫停下載任務(wù)后,如果 downloadTask(下載任務(wù))為非空档桃,調(diào)用 cancelByProducingResumeData:(void (^)(NSData *resumeData))completionHandler 這個方法枪孩,這個方法接收一個參數(shù),完成處理代碼塊藻肄,這個代碼塊有一個 NSData 參數(shù) resumeData蔑舞,如果 resumeData 非空,我們就保存這個對象到視圖控制器的 resumeData 屬性中嘹屯,在點擊再次下載時攻询,通過調(diào)用[ [self.session downloadTaskWithResumeData:self.resumeData] resume]方法進(jìn)行繼續(xù)下載操作。

經(jīng)過以上比較可以發(fā)現(xiàn)州弟,使用 NSURLSession 進(jìn)行斷點下載更加便捷钧栖。

配置信息

NSURLSession 的構(gòu)造方法sessionWithConfiguration:delegate:delegateQueue中有一個 NSURLSessionConfiguration 類的參數(shù)可以設(shè)置配置信息,其決定了 cookie婆翔,安全和高速緩存策略拯杠,最大主機(jī)連接數(shù),資源管理啃奴,網(wǎng)絡(luò)超時等配置潭陪。NSURLConnection 不能進(jìn)行這個配置,相比較與 NSURLConnection 依賴與一個全局的配置對象最蕾,缺乏靈活性而言依溯,NSURLSession 有很大的改進(jìn)了。

NSURLConnection

一瘟则、基本介紹

1. 異步處理代理

其中黎炉,NSURLRequest 被傳遞給 NSURLConnection。被委托對象(遵守以前的非正式協(xié)議 NSURLConnectionDelegate 和 NSURLConnectionDataDelegate )異步返回一個 NSURLResponse 以及包含服務(wù)器返回信息的 NSData醋拧。

2. 請求策略

在一個請求被發(fā)送到服務(wù)器之前拜隧,系統(tǒng)會先查詢共享的緩存信息,然后根據(jù)策略(policy)以及可用性(availability)的不同趁仙,一個已經(jīng)被緩存的響應(yīng)可能會被立即返回。如果沒有緩存的響應(yīng)可用垦页,則這個請求將根據(jù)我們指定的策略來緩存它的響應(yīng)以便將來的請求可以使用雀费。

3. 認(rèn)證策略

在把請求發(fā)送給服務(wù)器的過程中,服務(wù)器可能會發(fā)出鑒權(quán)查詢(authentication challenge)痊焊,這可以由共享的 cookie 或機(jī)密存儲(credential storage)來自動響應(yīng)盏袄,或者由被委托對象來響應(yīng)忿峻。發(fā)送中的請求也可以被注冊的 NSURLProtocol 對象所攔截,以便在必要的時候無縫地改變其加載行為辕羽。

4. 下載過程

NSURLConnection下載文件時逛尚,先是將整個文件下載到內(nèi)存,然后再寫入到沙盒刁愿,如果文件比較大绰寞,就會出現(xiàn)內(nèi)存暴漲的情況。

二铣口、使用步驟

請求步驟:

  • 設(shè)置請求路徑
  • 創(chuàng)建請求對象(默認(rèn)是GET請求滤钱,且已經(jīng)默認(rèn)包含了請求頭)
  • 發(fā)送網(wǎng)絡(luò)請求
  • 接收到服務(wù)器的響應(yīng)后,解析響應(yīng)體

1. 常用代理介紹

/*
 1.當(dāng)接收到服務(wù)器響應(yīng)的時候調(diào)用脑题,該方法只會調(diào)用一次
 第一個參數(shù)connection:監(jiān)聽的是哪個NSURLConnection對象
 第二個參數(shù)response:接收到的服務(wù)器返回的響應(yīng)頭信息
 */
- (void)connection:(nonnull NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response
/*
 2.當(dāng)接收到數(shù)據(jù)的時候調(diào)用件缸,該方法會被調(diào)用多次
 第一個參數(shù)connection:監(jiān)聽的是哪個NSURLConnection對象
 第二個參數(shù)data:本次接收到的服務(wù)端返回的二進(jìn)制數(shù)據(jù)(可能是片段)
 */
- (void)connection:(nonnull NSURLConnection *)connection didReceiveData:(nonnull NSData *)data
 /*
 3.當(dāng)服務(wù)端返回的數(shù)據(jù)接收完畢之后會調(diào)用
 通常在該方法中解析服務(wù)器返回的數(shù)據(jù)
 */
- (void)connectionDidFinishLoading:(nonnull NSURLConnection *)connection
/*
 4.當(dāng)請求錯誤的時候調(diào)用(比如請求超時)
 第一個參數(shù)connection:NSURLConnection對象
 第二個參數(shù):網(wǎng)絡(luò)請求的錯誤信息,如果請求失敗叔遂,則error有值
 */
- (void)connection:(nonnull NSURLConnection *)connection didFailWithError:(nonnull NSError *)error

2. 各種方法請求

方法一:利用 sendSynchronousRequest發(fā)送 GET 請求他炊,該請求是同步的

//1.確定請求路徑
NSURL *url = [NSURL URLWithString:@""];
    
//2.創(chuàng)建一個請求對象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
//3.把請求發(fā)送給服務(wù)器
//sendSynchronousRequest  阻塞式的方法,會卡住線程
    
NSHTTPURLResponse *response = nil;
NSError *error = nil;
    
/*
 第一個參數(shù):請求對象
 第二個參數(shù):響應(yīng)頭信息已艰,當(dāng)該方法執(zhí)行完畢之后痊末,該參數(shù)被賦值
 第三個參數(shù):錯誤信息,如果請求失敗旗芬,則error有值
 */
 //該方法是阻塞式的舌胶,會卡住線程
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

方法二:利用 sendAsynchronousRequest 發(fā)送 POST 請求,該請求是異步的

//1.確定請求路徑
NSURL *url = [NSURL URLWithString:@""];
    
//2.創(chuàng)建一個請求對象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    
// 2.1設(shè)置請求方式
// 注意: POST一定要大寫
request.HTTPMethod = @"POST";
// 2.2設(shè)置請求體
// 注意: 如果是給POST請求傳遞參數(shù): 那么不需要寫?號
request.HTTPBody = [@"username=Mitchell&pwd=123456&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
    
//3.把請求發(fā)送給服務(wù)器,發(fā)送一個異步請求
/*
 第一個參數(shù):請求對象
 第二個參數(shù):回調(diào)方法在哪個線程中執(zhí)行疮丛,如果是主隊列則block在主線程中執(zhí)行幔嫂,非主隊列則在子線程中執(zhí)行
 第三個參數(shù): completionHandlerBlock塊:接受到響應(yīng)的時候執(zhí)行該block中的代碼
            response:響應(yīng)頭信息
            data:響應(yīng)體
            connectionError:錯誤信息,如果請求失敗誊薄,那么該參數(shù)有值
 */
 
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError * __nullable connectionError) {

    //4.解析服務(wù)器返回的數(shù)據(jù)
    NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    //轉(zhuǎn)換并打印響應(yīng)頭信息
    NSHTTPURLResponse *r = (NSHTTPURLResponse *)response;
}];

方法三:通過設(shè)置代理履恩,來處理請求響應(yīng)或數(shù)據(jù)

// 第一種代理方式,自動發(fā)送請求
[[NSURLConnection alloc]initWithRequest:request delegate:self];
    
/*
第一個參數(shù):請求對象
第二個參數(shù):誰成為NSURLConnetion對象的代理
第三個參數(shù):是否馬上發(fā)送網(wǎng)絡(luò)請求呢蔫,如果該值為YES則立刻發(fā)送切心,如果為NO則不會發(fā)送網(wǎng)路請求
*/
NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
    
//在startImmediately為NO時,調(diào)用該方法控制網(wǎng)絡(luò)請求的發(fā)送
[conn start];
    
// 第三種代理方式
//設(shè)置代理的第三種方式:使用類方法設(shè)置代理片吊,會自動發(fā)送網(wǎng)絡(luò)請求
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
    
//取消網(wǎng)絡(luò)請求
//[conn cancel];

3. NSURLConnection 與 NSRunLoop 的關(guān)聯(lián)使用

主要是區(qū)分 NSURLConnection 在主線程和子線程發(fā)送網(wǎng)絡(luò)請求的區(qū)別

  • 主線程

    // 直接發(fā)送網(wǎng)絡(luò)請求绽昏,發(fā)送是異步的,但是代理方法是在主線程中執(zhí)行的
    
    //這里分兩種方式發(fā)送請求
    //2.1 直接發(fā)送網(wǎng)絡(luò)請求是異步的俏脊,但是回調(diào)方法是在主線程中執(zhí)行的
    //[[NSURLConnection alloc]initWithRequest:request delegate:self];
    
    // 如果按照如下設(shè)置全谤,那么回調(diào)的代理方法也會運行在子線程中
    NSURL *url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    //2.2 設(shè)置回調(diào)方法也在子線程中運行
    NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
    [conn setDelegateQueue:[[NSOperationQueue alloc] init]];
    [conn start];
    
  • 子線程

    因為 NSURLConnection 是局部變量,當(dāng)我們創(chuàng)建的時候其實是會默認(rèn)添加到當(dāng)前的 RunLoop 中爷贫,如果是在主線程添加认然,主線程的 RunLoop 是默認(rèn)有的补憾,無須我們創(chuàng)建;然而如果在子線程中卷员,是默認(rèn)沒有 RunLoop 和輸入源的盈匾,所以需要給子線程手動添加 RunLoop。
    調(diào)用 start 方法時毕骡,如果沒有 RunLoop 削饵,會默認(rèn)添加一個 RunLoop 到當(dāng)前的線程中來,然后將 connection 加到 RunLoop 中挺峡。

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
        NSRunLoop *loop = [NSRunLoop currentRunLoop];
        [NSURLConnection connectionWithRequest:request delegate:self];
        [loop run];
        
        // 下面這樣無法發(fā)送請求
        // [NSURLConnection connectionWithRequest:request delegate:self];
    });
    

NSURLConnection 的用法基本就這個這么多葵孤。

NSURLSession

一、 session類型

Default session

+defaultSessionConfiguration 返回一個標(biāo)準(zhǔn)的 configuration橱赠,這個配置實際上與 NSURLConnection 的網(wǎng)絡(luò)堆棧(networking stack)是一樣的尤仍,具有相同的共享 NSHTTPCookieStorage,共享 NSURLCache 和共享 NSURLCredentialStorage狭姨。

Ephemeral session

+ephemeralSessionConfiguration 返回一個預(yù)設(shè)配置宰啦,這個配置中不會對緩存Cookie 和證書進(jìn)行持久性的存儲,這對于實現(xiàn)像秘密瀏覽這種功能來說是很理想的饼拍。

Background session

+backgroundSessionConfiguration:(NSString *)identifier 的獨特之處在于赡模,它會創(chuàng)建一個后臺 session。后臺 session 不同于常規(guī)的师抄,普通的 session漓柑,它甚至可以在應(yīng)用程序掛起,退出或者崩潰的情況下進(jìn)行上傳和下載任務(wù)叨吮。初始化時指定的標(biāo)識符辆布,被用于向任何可能在進(jìn)程外恢復(fù)后臺傳輸?shù)氖刈o(hù)進(jìn)程。

二茶鉴、配置屬性

基本配置

HTTPAdditionalHeaders 指定了一組默認(rèn)的可以設(shè)置請求(outbound request)的數(shù)據(jù)頭锋玲。這對于跨 session 共享信息,如內(nèi)容類型涵叮、語言惭蹂、用戶代理和身份認(rèn)證,是很有用的割粮。

// 設(shè)置請求的header
NSString *userPasswordString = [NSString stringWithFormat:@"%@:%@", user, password];
NSData * userPasswordData = [userPasswordString dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64EncodedCredential = [userPasswordData base64EncodedStringWithOptions:0];
NSString *authString = [NSString stringWithFormat:@"Basic %@", base64EncodedCredential];
NSString *userAgentString = @"AppName/com.example.app (iPhone 5s; iOS 7.0.2; Scale/2.0)";

configuration.HTTPAdditionalHeaders = @{@"Accept": @"application/json",
                                        @"Accept-Language": @"en",
                                        @"Authorization": authString,
                                        @"User-Agent": userAgentString};
  • networkServiceType 對標(biāo)準(zhǔn)的網(wǎng)絡(luò)流量盾碗、網(wǎng)絡(luò)電話、語音舀瓢、視頻置尔,以及由一個后臺進(jìn)程使用的流量進(jìn)行了區(qū)分。大多數(shù)應(yīng)用程序都不需要設(shè)置這個。

  • allowsCellularAccess 和 discretionary 被用于節(jié)省通過蜂窩網(wǎng)絡(luò)連接的帶寬榜轿。對于后臺傳輸?shù)那闆r,推薦大家使用 discretionary 這個屬性朵锣,而不是 allowsCellularAccess谬盐,因為前者會把 WiFi 和電源的可用性考慮在內(nèi)。

  • timeoutIntervalForRequest 和 timeoutIntervalForResource 分別指定了對于請求和資源的超時間隔诚些。許多開發(fā)人員試圖使用 timeoutInterval 去限制發(fā)送請求的總時間飞傀,但其實它真正的含義是:分組(packet)之間的時間。實際上我們應(yīng)該使用 timeoutIntervalForResource 來規(guī)定整體超時的總時間诬烹,但應(yīng)該只將其用于后臺傳輸砸烦,而不是用戶實際上可能想要去等待的任何東西。

  • HTTPMaximumConnectionsPerHost 是 Foundation 框架中 URL 加載系統(tǒng)的一個新的配置選項绞吁。它曾經(jīng)被 NSURLConnection 用于管理私有的連接池〈倍唬現(xiàn)在有了 NSURLSession,開發(fā)者可以在需要時限制連接到特定主機(jī)的數(shù)量家破。

  • HTTPShouldUsePipelining 這個屬性在 NSMutableURLRequest 下也有颜说,它可以被用于開啟 HTTP 管線化(HTTP pipelining),這可以顯著降低請求的加載時間汰聋,但是由于沒有被服務(wù)器廣泛支持门粪,默認(rèn)是禁用的。

  • sessionSendsLaunchEvents 是另一個新的屬性烹困,該屬性指定該 session 是否應(yīng)該從后臺啟動玄妈。

  • connectionProxyDictionary 指定了 session 連接中的代理服務(wù)器。同樣地髓梅,大多數(shù)面向消費者的應(yīng)用程序都不需要代理拟蜻,所以基本上不需要配置這個屬性。

Cookie 策略

  • HTTPCookieStorage 存儲了 session 所使用的 cookie女淑。默認(rèn)情況下會使用 NSHTTPCookieShorage 的 +sharedHTTPCookieStorage 這個單例對象瞭郑,這與 NSURLConnection 是相同的。

  • HTTPCookieAcceptPolicy 決定了什么情況下 session 應(yīng)該接受從服務(wù)器發(fā)出的 cookie鸭你。

  • HTTPShouldSetCookies 指定了請求是否應(yīng)該使用 session 存儲的 cookie屈张,即 HTTPCookieSorage 屬性的值。

安全策略

  • URLCredentialStorage 存儲了 session 所使用的證書袱巨。默認(rèn)情況下會使用 NSURLCredentialStorage 的 +sharedCredentialStorage 這個單例對象阁谆,這與 NSURLConnection 是相同的。

  • TLSMaximumSupportedProtocol 和 TLSMinimumSupportedProtocol 確定 `session 是否支持 SSL 協(xié)議愉老。

緩存策略

  • URLCache 是 session 使用的緩存场绿。默認(rèn)情況下會使用 NSURLCache 的 +sharedURLCache 這個單例對象,這與 NSURLConnection 是相同的嫉入。

  • requestCachePolicy 指定了一個請求的緩存響應(yīng)應(yīng)該在什么時候返回焰盗。這相當(dāng)于 NSURLRequest 的 -cachePolicy 方法璧尸。

自定義協(xié)議

protocolClasses 用來配置特定某個 session 所使用的自定義協(xié)議(該協(xié)議是 NSURLProtocol 的子類)的數(shù)組。

三熬拒、NSURLSessionTask

NSURLsessionTask 是一個抽象類爷光,其下有 3 個實體子類可以直接使用:NSURLSessionDataTask、NSURLSessionUploadTask澎粟、NSURLSessionDownloadTask蛀序。這 3 個子類封裝了現(xiàn)代程序三個最基本的網(wǎng)絡(luò)任務(wù):獲取數(shù)據(jù),比如 JSON 或者 XML活烙,上傳文件和下載文件徐裸。

不同于直接使用 alloc-init 初始化方法,task 是由一個 NSURLSession 創(chuàng)建的啸盏。每個 task 的構(gòu)造方法都對應(yīng)有或者沒有 completionHandler 這個 block 的兩個版本重贺。

四、代理

針對NSURLsessionTask的代理宫补,根代理為NSURLSessionDelegate檬姥,其它的代理直接或者間接繼承自改代理,如:NSURLSessionTaskDelegate粉怕、NSURLSessionDataDelegate健民、NSURLSessionDownloadDelegate。其中根代理NSURLSessionDelegate主要處理鑒權(quán)贫贝、后臺下載任務(wù)完成通知等等秉犹,NSURLSessionTaskDelegate主要處理收到鑒權(quán)響應(yīng)、任務(wù)結(jié)束(無論是正常還是異常)稚晚,NSURLSessionDataDelegate處理數(shù)據(jù)的接收崇堵、dataTask轉(zhuǎn)downloadTask、緩存等客燕,NSURLSessionDownloadDelegate主要處理數(shù)據(jù)下載鸳劳、數(shù)據(jù)進(jìn)度通知等。

五也搓、NSURLSession 應(yīng)用

1. NSURLSessionDataTask 發(fā)送 GET 請求

//確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520&pwd=520&type=JSON"];
//創(chuàng)建 NSURLSession 對象
NSURLSession *session = [NSURLSession sharedSession];

/**
根據(jù)對象創(chuàng)建 Task 請求赏廓,默認(rèn)在子線程中解析數(shù)據(jù)

url  方法內(nèi)部會自動將 URL 包裝成一個請求對象(默認(rèn)是 GET 請求)
completionHandler  完成之后的回調(diào)(成功或失敗)

param data     返回的數(shù)據(jù)(響應(yīng)體)
param response 響應(yīng)頭
param error    錯誤信息
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:
         ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

    //解析服務(wù)器返回的數(shù)據(jù)
    NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
//發(fā)送請求(執(zhí)行Task)
[dataTask resume];

2. NSURLSessionDataTask 發(fā)送 POST 請求

//確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//創(chuàng)建可變請求對象
NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:url];
//修改請求方法
requestM.HTTPMethod = @"POST";
//設(shè)置請求體
requestM.HTTPBody = [@"username=520&pwd=520&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
//創(chuàng)建會話對象
NSURLSession *session = [NSURLSession sharedSession];
//創(chuàng)建請求 Task
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:requestM completionHandler:
         ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

    //解析返回的數(shù)據(jù)
    NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
//發(fā)送請求
[dataTask resume];

3. NSURLSessionDataTask 設(shè)置代理發(fā)送請求

//確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//創(chuàng)建可變請求對象
NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:url];
//設(shè)置請求方法
requestM.HTTPMethod = @"POST";
//設(shè)置請求體
requestM.HTTPBody = [@"username=520&pwd=520&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
//創(chuàng)建會話對象傍妒,設(shè)置代理
/**
第一個參數(shù):配置信息
第二個參數(shù):設(shè)置代理
第三個參數(shù):隊列幔摸,如果該參數(shù)傳遞nil 那么默認(rèn)在子線程中執(zhí)行
*/
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                      delegate:self delegateQueue:nil];
//創(chuàng)建請求 Task
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:requestM];
//發(fā)送請求
[dataTask resume];

代理方法:

- (void)URLSession:(NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask 
didReceiveResponse:(nonnull NSURLResponse *)response 
completionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler 
{
     //子線程中執(zhí)行
     NSLog(@"接收到服務(wù)器響應(yīng)的時候調(diào)用 -- %@", [NSThread currentThread]);

     self.dataM = [NSMutableData data];
     //默認(rèn)情況下不接收數(shù)據(jù)
     //必須告訴系統(tǒng)是否接收服務(wù)器返回的數(shù)據(jù)
     completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data 
{
     NSLog(@"接受到服務(wù)器返回數(shù)據(jù)的時候調(diào)用,可能被調(diào)用多次");
     //拼接服務(wù)器返回的數(shù)據(jù)
     [self.dataM appendData:data];
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
     NSLog(@"請求完成或者是失敗的時候調(diào)用");
     //解析服務(wù)器返回數(shù)據(jù)
     NSLog(@"%@", [[NSString alloc] initWithData:self.dataM encoding:NSUTF8StringEncoding]);
}

設(shè)置代理之后的強(qiáng)引用問題

  • NSURLSession 對象在使用的時候,如果設(shè)置了代理颤练,那么 session 會對代理對象保持一個強(qiáng)引用既忆,在合適的時候應(yīng)該主動進(jìn)行釋放
  • 可以在控制器調(diào)用 viewDidDisappear 方法的時候來進(jìn)行處理,通過調(diào)用 invalidateAndCancel 方法或者是 finishTasksAndInvalidate 方法來釋放對代理對象的強(qiáng)引用。

其中患雇,invalidateAndCancel 是直接取消請求然后釋放代理對象跃脊,而finishTasksAndInvalidate 是等請求完成之后釋放代理對象。

4. NSURLSessionDownloadTask 簡單下載

//確定請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"];
//創(chuàng)建請求對象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//創(chuàng)建會話對象
NSURLSession *session = [NSURLSession sharedSession];
//創(chuàng)建會話請求
//優(yōu)點:該方法內(nèi)部已經(jīng)完成了邊接收數(shù)據(jù)邊寫沙盒的操作苛吱,解決了內(nèi)存飆升的問題
NSURLSessionDownloadTask *downTask = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {

    //默認(rèn)存儲到臨時文件夾 tmp 中匾乓,需要剪切文件到 cache
    NSLog(@"%@", location);//目標(biāo)位置
    NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]  
                         stringByAppendingPathComponent:response.suggestedFilename];
    
     /**
      fileURLWithPath:有協(xié)議頭
      URLWithString:無協(xié)議頭
      */
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];

}];
//發(fā)送請求
[downTask resume];

5. NSURLSessionDownloadTask 代理方式

NSURL *url = [NSURL URLWithString:@"http://e.hiphotos.baidu.com/image/pic/item/63d0f703918fa0ec14b94082249759ee3c6ddbc6.jpg"];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate:self delegateQueue: [NSOperationQueue mainQueue]];

NSURLSessionDownloadTask * downloadTask =[ defaultSession downloadTaskWithURL:url];
[downloadTask resume];

代理方法:

// 接收數(shù)據(jù),可能多次被調(diào)用
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    float progress = totalBytesWritten * 1.0/totalBytesExpectedToWrite;
    
    // 主線程更新UI
    dispatch_async(dispatch_get_main_queue(),^ {
        [self.progressView setProgress:progress animated:YES];
    });
}

// 3.下載完成之后調(diào)用該方法
-  (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    NSString *catchDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSString *filePath = [catchDir stringByAppendingPathComponent:@"app.dmg"];
    
    NSError *fileError = nil;
    NSURL *fileURL = [NSURL fileURLWithPath:filePath];
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&fileError];
    
    if (fileError) {
        NSLog(@"保存下載文件出錯:%@", fileError);
    } else {
        NSLog(@"保存成功:%@", filePath);
    }
}

暫停和恢復(fù)下載:

方式一:

// 暫停
- (IBAction)suspendDownload 
{
    __weak typeof(self) weakSelf = self;
    [self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
        weakSelf.resumeData = resumeData;
    }];
}

// 開始
- (IBAction)resumeDownload 
{
    if (self.resumeData)
    {
        self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
        [self.downloadTask resume];
    }
    else
    {
        [self.downloadTask resume];
    }
}

方式二:

//暫停
[self.downloadTask suspend];
//開始
[self.downloadTask resume];

6. NSURLSessionDownloadTask 后臺下載

  1. 創(chuàng)建 NSURLSession 時又谋,需要創(chuàng)建后臺模式 NSURLSessionConfiguration

    NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"BackgroundIdentifier"];
    
  1. 在AppDelegate中實現(xiàn)下面方法,并定義變量保存completionHandler代碼塊:

    // 應(yīng)用處于后臺娱局,所有下載任務(wù)完成調(diào)用
    - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
    {
        _backgroundSessionCompletionHandler = completionHandler;
    }
    
  2. 在下載類中實現(xiàn)下面NSURLSessionDelegate協(xié)議方法彰亥,其實就是先執(zhí)行完task的協(xié)議,保存數(shù)據(jù)衰齐、刷新界面之后再執(zhí)行在AppDelegate中保存的代碼塊:

    // 應(yīng)用處于后臺任斋,所有下載任務(wù)完成及NSURLSession協(xié)議調(diào)用之后調(diào)用
    - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
            if (appDelegate.backgroundSessionCompletionHandler) 
            {
                void (^completionHandler)(void) = appDelegate.backgroundSessionCompletionHandler;
                appDelegate.backgroundSessionCompletionHandler = nil;
            
                // 執(zhí)行block,釋放阻止應(yīng)用掛起的斷言
                completionHandler();
            }
        });
    }   
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耻涛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌终佛,老刑警劉巖趟径,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異卓研,居然都是意外死亡趴俘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門奏赘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寥闪,“玉大人,你說我怎么就攤上這事磨淌∑1铮” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵梁只,是天一觀的道長缚柳。 經(jīng)常有香客問我,道長敛纲,這世上最難降的妖魔是什么喂击? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮淤翔,結(jié)果婚禮上翰绊,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好监嗜,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布谐檀。 她就那樣靜靜地躺著,像睡著了一般裁奇。 火紅的嫁衣襯著肌膚如雪桐猬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天刽肠,我揣著相機(jī)與錄音溃肪,去河邊找鬼。 笑死音五,一個胖子當(dāng)著我的面吹牛惫撰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播躺涝,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼厨钻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坚嗜?” 一聲冷哼從身側(cè)響起夯膀,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苍蔬,沒想到半個月后诱建,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡银室,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年涂佃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜈敢。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡辜荠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抓狭,到底是詐尸還是另有隱情伯病,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布否过,位于F島的核電站午笛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏苗桂。R本人自食惡果不足惜药磺,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望煤伟。 院中可真熱鬧癌佩,春花似錦木缝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至姚建,卻和暖如春矫俺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背掸冤。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工厘托, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人稿湿。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓催烘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缎罢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容