網(wǎng)絡(luò)請(qǐng)求 - NSURLSession

在WWDC 2013中,Apple的團(tuán)隊(duì)對(duì)NSURLConnection進(jìn)行了重構(gòu)站蝠,并推出了NSURLSession作為替代汰具。

NSURLSession也是一組相互依賴的類,它的大部分組件和NSURLConnection中的組件相同如NSURLRequest菱魔,NSURLCache等留荔。而NSURLSession的不同之處在于,它將NSURLConnection替換為NSURLSession和NSURLSessionConfiguration澜倦,以及3個(gè)NSURLSessionTask的子類:NSURLSessionDataTask, NSURLSessionUploadTask, 和NSURLSessionDownloadTask聚蝶。

URL加載框架

NSRULSession 網(wǎng)絡(luò)請(qǐng)求系統(tǒng)包括一個(gè)session、一個(gè)configuration藻治、一個(gè)Task已經(jīng)Task附帶的delegate碘勉。一個(gè)NSURLSessionConfiguration,總共有三種模式。一個(gè)NSURLSessionTask桩卵。NSURLSessionTask是抽閑類,對(duì)應(yīng)的代理NSURLSessionTaskDelegate恰聘。我們具體使用的時(shí)候,會(huì)使用他的三種子類吸占,而且每個(gè)子類都有對(duì)應(yīng)的delegate晴叨。

初始化方法:

+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;

屬性:
delegate: 創(chuàng)建 session 時(shí)傳入的 delegate,session 對(duì)delegate 強(qiáng)引用矾屯,只有當(dāng) session 執(zhí)行 invalidate 相關(guān)方法后兼蕊,才會(huì)釋放引用

delegateQueue: 創(chuàng)建 session 時(shí)傳入的 delegateQueue,所有的代理方法和 block 回調(diào)都會(huì)在這個(gè) queue 中執(zhí)行件蚕。

sessionDescription: 可給 session 設(shè)置一個(gè)描述孙技,用于調(diào)試或界面顯示。

@property (readonly, retain) NSOperationQueue *delegateQueue;
@property (nullable, readonly, retain) id <NSURLSessionDelegate> delegate;
@property (nullable, copy) NSString *sessionDescription;

創(chuàng)建 task:

可參考NSURLSession

NSURLSession創(chuàng)建Task.png

block形式創(chuàng)建并執(zhí)行block,和delegate一樣排作,Block也有dataTask系列牵啦、downloadTask系列、uploadTask系列

block創(chuàng)建Task.png

獲取 session 中的 task:

- (void)getTasksWithCompletionHandler:(void (^)(NSArray<NSURLSessionDataTask *> *dataTasks, NSArray<NSURLSessionUploadTask *> *uploadTasks, NSArray<NSURLSessionDownloadTask *> *downloadTasks))completionHandler;
- (void)getAllTasksWithCompletionHandler:(void (^)(NSArray<__kindof NSURLSessionTask *> *tasks))completionHandler;

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

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

invalidateAndCancel 方法直接取消請(qǐng)求然后釋放代理對(duì)象,
finishTasksAndInvalidate 方法等請(qǐng)求完成之后釋放代理對(duì)象

- (void)finishTasksAndInvalidate;
- (void)invalidateAndCancel;

一裳瘪、NSURLSessionConfiguration類

里面所有屬性的設(shè)置都是對(duì)當(dāng)前NSURLSession的整體設(shè)置土浸。NSURLSessionConfiguration會(huì)話配置

默認(rèn)會(huì)話模式(default):工作模式類似于原來的NSURLConnection,使用的是基于磁盤緩存的持久化策略彭羹,使用用戶keychain中保存的證書進(jìn)行認(rèn)證授權(quán)黄伊。

瞬時(shí)會(huì)話模式(ephemeral):該模式不使用磁盤保存任何數(shù)據(jù)。所有和會(huì)話相關(guān)的caches派殷,證書还最,cookies等都被保存在RAM中,因此當(dāng)程序使會(huì)話無效毡惜,這些緩存的數(shù)據(jù)就會(huì)被自動(dòng)清空拓轻。

后臺(tái)會(huì)話模式(background):該模式在后臺(tái)完成上傳和下載,在創(chuàng)建Configuration對(duì)象的時(shí)候需要提供一個(gè)NSString類型的ID用于標(biāo)識(shí)完成工作的后臺(tái)會(huì)話虱黄。

+ (NSURLSessionConfiguration *)defaultSessionConfiguration; 

+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration; 
 
+ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;
//方法中的identifier參數(shù)指定了會(huì)話的ID悦即,用于標(biāo)記后臺(tái)的session吮成。

timeoutIntervalForRequest: 指定了請(qǐng)求以及該資源的超時(shí)時(shí)間間隔, 當(dāng)新數(shù)據(jù)到達(dá)時(shí)橱乱,與該值相關(guān)聯(lián)的計(jì)時(shí)器將被重置。默認(rèn)60s
timeoutIntervalForResource: 這應(yīng)該只用于后臺(tái)傳輸,默認(rèn)7天

@property NSTimeInterval timeoutIntervalForRequest;

@property NSTimeInterval timeoutIntervalForResource;

networkServiceType對(duì)標(biāo)準(zhǔn)的網(wǎng)絡(luò)流量粱甫,網(wǎng)絡(luò)電話泳叠,語音,視頻茶宵,以及由一個(gè)后臺(tái)進(jìn)程使用的流量進(jìn)行了區(qū)分危纫。設(shè)置后便于系統(tǒng)對(duì)網(wǎng)絡(luò)請(qǐng)求的區(qū)分,系統(tǒng)能根據(jù)提供的信息來優(yōu)化網(wǎng)絡(luò)處理乌庶,從而優(yōu)化電池壽命种蝶,網(wǎng)絡(luò)性能等等

@property NSURLRequestNetworkServiceType networkServiceType;

allowsCellularAccess: 屬性指定是否允許使用蜂窩連接;
discretionary: 屬性為YES時(shí)表示當(dāng)程序在background運(yùn)作時(shí)由系統(tǒng)自己選擇最佳的網(wǎng)絡(luò)連接配置,該屬性可以節(jié)省通過蜂窩連接的帶寬瞒大。在使用后臺(tái)傳輸數(shù)據(jù)的時(shí)候螃征,建議使用discretionary屬性,而不是allowsCellularAccess屬性透敌,因?yàn)樗鼤?huì)把WiFi和電源可用性考慮在內(nèi)盯滚。

@property BOOL allowsCellularAccess;  
 
@property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(NA, 7_0);

此屬性根據(jù)此配置確定會(huì)話中的任務(wù)對(duì)每個(gè)主機(jī)的最大并發(fā)連接數(shù)。MacOS中的默認(rèn)值為6酗电,iOS中為4.如果有多個(gè)會(huì)話魄藕,會(huì)超過這個(gè)限制

@property NSInteger HTTPMaximumConnectionsPerHost;

HTTPShouldUsePipelining表示receiver(理解為iOS客戶端)的下一個(gè)信息是否必須等到上一個(gè)請(qǐng)求回復(fù)才能發(fā)送。
如果為YES表示可以撵术,NO表示必須等receiver收到先前的回復(fù)才能發(fā)送下個(gè)信息背率。默認(rèn)為NO

@property BOOL HTTPShouldUsePipelining;

Cookies是一種能夠讓網(wǎng)站服務(wù)器把少量數(shù)據(jù)儲(chǔ)存到客戶端的硬盤或內(nèi)存,或是從客戶端的硬盤讀取數(shù)據(jù)的一種技術(shù)。Cookies是當(dāng)你瀏覽某網(wǎng)站時(shí)退渗,由Web服務(wù)器置于你硬盤上的一個(gè)非常小的文本文件移稳,它可以記錄你的用戶ID、密碼会油、瀏覽過的網(wǎng)頁个粱、停留的時(shí)間等信息。當(dāng)你再次來到該網(wǎng)站時(shí)翻翩,網(wǎng)站通過讀取Cookies都许,得知你的相關(guān)信息,就可以做出相應(yīng)的動(dòng)作嫂冻,如在頁面顯示歡迎你的標(biāo)語胶征,或者讓你不用輸入ID、密碼就直接登錄等等桨仿。默認(rèn)為YES

@property BOOL HTTPShouldSetCookies;

//Cookie數(shù)據(jù)的接收協(xié)議(接收所有睛低,不接收,默認(rèn)只接受主文檔)
@property NSHTTPCookieAcceptPolicy HTTPCookieAcceptPolicy;

//Cookie 處理類 [詳解](http://www.jb51.net/article/88173.htm)
@property (nullable, retain) NSHTTPCookieStorage *HTTPCookieStorage;

是會(huì)話使用的證書存儲(chǔ)服傍。默認(rèn)情況下钱雷,NSURLCredentialStorage 的+ sharedCredentialStorage 會(huì)被使用使用

@property (nullable, retain) NSURLCredentialStorage *URLCredentialStorage;

NSURLCache詳解和使用

@property (nullable, retain) NSURLCache *URLCache;

指定了一組默認(rèn)的可以設(shè)置出站請(qǐng)求的數(shù)據(jù)頭

@property (nullable, copy) NSDictionary *HTTPAdditionalHeaders;

NSHTTPCookieStorage

NSURLRequest都會(huì)幫你主動(dòng)記錄下來你訪問的站點(diǎn)設(shè)置的 cookie,而且很負(fù)責(zé)任的吹零,當(dāng)你下次再訪問這個(gè)站點(diǎn)時(shí)罩抗,NSURLRequest會(huì)拿著上次保存下來了的cookie繼續(xù)去請(qǐng)求。

NSHttpCookiesStorage是一個(gè)單例灿椅,管理所有的Cookie套蒂,每個(gè)Cookie都是一個(gè)NSHTTPCookie的實(shí)例,所有應(yīng)用的cookies都被保存在這個(gè)NSHTTPCookieStorage的單例中茫蛹,并且跨進(jìn)程同步操刀。 但為了安全,每個(gè)應(yīng)用都有自己的沙盒婴洼,A應(yīng)用的cookie是不能被B應(yīng)用訪問的骨坑。

查看cookie

NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [cookieJar cookies]) {
   NSLog(@"%@", cookie);
}

清空cookie

NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
  NSArray *_tmpArray = [NSArray arrayWithArray:[cookieJar cookies]];
  for (id obj in _tmpArray) {
    [cookieJar deleteCookie:obj];
  }

指定cookie

NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
  [cookieProperties setObject:@"username" forKey:NSHTTPCookieName];
  [cookieProperties setObject:@"rainbird" forKey:NSHTTPCookieValue];
  [cookieProperties setObject:@"cnrainbird.com" forKey:NSHTTPCookieDomain];
  [cookieProperties setObject:@"cnrainbird.com" forKey:NSHTTPCookieOriginURL];
  [cookieProperties setObject:@"/" forKey:NSHTTPCookiePath];
  [cookieProperties setObject:@"0" forKey:NSHTTPCookieVersion];
  NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
  [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];

NSURLCache

NSURLCache詳解和使用

在應(yīng)用程序的開發(fā)中,為了減小對(duì)網(wǎng)絡(luò)的依賴窃蹋,提高程序性能卡啰,常常會(huì)對(duì)一些非實(shí)時(shí)性的數(shù)據(jù)進(jìn)行緩存處理,NSURLCache類用于管理NSURLRequest請(qǐng)求緩存警没。

緩存的實(shí)現(xiàn)說明:由于GET請(qǐng)求一般用來查詢數(shù)據(jù)匈辱,POST請(qǐng)求一般是發(fā)大量數(shù)據(jù)給服務(wù)器處理(變動(dòng)性比較大),因此一般只對(duì)GET請(qǐng)求進(jìn)行緩存杀迹,而不對(duì)POST請(qǐng)求進(jìn)行緩存亡脸。

SURLCache的常見用法:

獲得全局緩存對(duì)象(沒必要手動(dòng)創(chuàng)建)

NSURLCache *cache = [NSURLCache sharedURLCache]; 

設(shè)置內(nèi)存緩存的最大容量(字節(jié)為單位,默認(rèn)為512KB)
設(shè)置硬盤緩存的最大容量 (字節(jié)為單位,默認(rèn)為10M)

@property NSUInteger memoryCapacity;
@property NSUInteger diskCapacity;
@property (readonly) NSUInteger currentMemoryUsage;

取得某個(gè)請(qǐng)求的緩存浅碾,存儲(chǔ)緩存

- (nullable NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request;
- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request;

清除緩存

- (void)removeCachedResponseForRequest:(NSURLRequest *)request;
- (void)removeAllCachedResponses;
- (void)removeCachedResponsesSinceDate:(NSDate *)date NS_AVAILABLE(10_10, 8_0);

二大州、NSURLSessionTask類

可以參考這篇文章AFNetWorking源碼之NSURLSession系列概述

從上面我們發(fā)現(xiàn)Task和delegate有一套對(duì)應(yīng)的繼承關(guān)系:

對(duì)應(yīng)的繼承關(guān)系.png

從繼承關(guān)系上,我們就可以理解在初始化的時(shí)候垂谢,只通過設(shè)置NSURLSession對(duì)象的delegate就可以了厦画。因?yàn)楦鶕?jù)不同的task,其實(shí)就是設(shè)置了不同的delegate滥朱。這個(gè)設(shè)計(jì)避免了多次設(shè)置delegate的情況根暑,同時(shí)也根據(jù)不同的task實(shí)現(xiàn)不同的delegate方法。

NSURLSessionTask的屬性和方法

@property (readonly)                 NSUInteger    taskIdentifier;
@property (nullable, readonly, copy) NSURLRequest  *originalRequest;
@property (nullable, readonly, copy) NSURLRequest  *currentRequest;
@property (nullable, readonly, copy) NSURLResponse *response;

//task優(yōu)先級(jí)
@property float priority

//當(dāng)前接收量
@property (readonly) int64_t countOfBytesReceived;

//當(dāng)前發(fā)送量
@property (readonly) int64_t countOfBytesSent;

//發(fā)送數(shù)據(jù)總數(shù)
@property (readonly) int64_t countOfBytesExpectedToSend;

//接收數(shù)據(jù)總數(shù)徙邻,來源于響應(yīng)頭Content-Length字段
@property (readonly) int64_t countOfBytesExpectedToReceive;

//cancel方法可以取消當(dāng)前的任務(wù),你也可以向處于suspend狀態(tài)的任務(wù)發(fā)送cancel消息,任務(wù)如果被取消便不能再恢復(fù)到之前的狀態(tài).
- (void)cancel;

//suspend可以讓當(dāng)前的任務(wù)暫停
- (void)suspend;

//resume方法不僅可以啟動(dòng)任務(wù),還可以喚醒suspend狀態(tài)的任務(wù)
- (void)resume;

1排嫌、代理說明

NSURLSessionDelegate

當(dāng)一個(gè)session遇到系統(tǒng)錯(cuò)誤或者未檢測(cè)到的錯(cuò)誤的時(shí)候,就會(huì)調(diào)用這個(gè)方法缰犁。

- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error

當(dāng)請(qǐng)求需要認(rèn)證淳地、或者h(yuǎn)ttps證書認(rèn)證的時(shí)候,我們就需要在這個(gè)方法里面處理帅容。

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler

如果應(yīng)用進(jìn)入后臺(tái)颇象、這個(gè)方法會(huì)被調(diào)用。我們?cè)谶@里可以對(duì)session發(fā)起的請(qǐng)求做各種操作比如請(qǐng)求完成的回調(diào)等丰嘉。

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session 

NSURLSessionTaskDelegate

當(dāng)請(qǐng)求重定向的時(shí)候調(diào)用這個(gè)方法夯到。我們必須設(shè)置一個(gè)新的NSURLRequest對(duì)象傳入completionHandler來重定向新的請(qǐng)求嚷缭,但是當(dāng)session是background模式的時(shí)候饮亏,這個(gè)方法不會(huì)被調(diào)用。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
        newRequest:(NSURLRequest *)request
 completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler{

}

當(dāng)請(qǐng)求需要認(rèn)證的時(shí)候調(diào)用這個(gè)方法阅爽。如果沒有實(shí)現(xiàn)這個(gè)代理路幸,那么請(qǐng)求認(rèn)證這個(gè)過程不會(huì)被調(diào)用。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{

}

如果請(qǐng)求需要一個(gè)新的請(qǐng)求體時(shí)付翁,這個(gè)方法就會(huì)被調(diào)用简肴。比如認(rèn)證失敗的時(shí)候,我們可以通過這個(gè)機(jī)會(huì)從新認(rèn)證百侧。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
 needNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler
 

當(dāng)我們上傳數(shù)據(jù)的時(shí)候砰识,我們可以通過這個(gè)代理方法獲取上傳進(jìn)度。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
   didSendBodyData:(int64_t)bytesSent
    totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend

當(dāng)task的統(tǒng)計(jì)信息收集好了以后佣渴,調(diào)用這個(gè)方法辫狼。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics 

當(dāng)一個(gè)task出錯(cuò)的時(shí)候,會(huì)調(diào)用這個(gè)方法辛润。如果error是nil膨处,也會(huì)調(diào)用這個(gè)方法,表示task完成。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error{
    NSLog(@"數(shù)據(jù)返回以后真椿,不管有錯(cuò)沒錯(cuò)都回調(diào)用鹃答,如果沒錯(cuò),error及時(shí)nil");
}

NSURLSessionDataDelegate

當(dāng)一個(gè)task接收到返回信息突硝。當(dāng)所有信息都接收完畢以后测摔,completionHandler會(huì)被調(diào)用。我們可以在這里取消一個(gè)網(wǎng)絡(luò)請(qǐng)求或者把一個(gè)datatask轉(zhuǎn)換為downloadtask解恰。如果沒有實(shí)現(xiàn)這個(gè)代理方法避咆,我們也可以通過task的response屬性獲取到對(duì)應(yīng)的數(shù)據(jù)。background模式的uploadtask不會(huì)調(diào)用這個(gè)方法修噪。

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
 

當(dāng)一個(gè)datatask轉(zhuǎn)換為一個(gè)downloadtask以后會(huì)調(diào)用查库。

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask{
    // 允許處理服務(wù)器的響應(yīng),才會(huì)繼續(xù)接收服務(wù)器返回的數(shù)據(jù)
    completionHandler(NSURLSessionResponseAllow);
}

當(dāng)data可以使用的時(shí)候黄琼,調(diào)用這個(gè)方法樊销。我們可以在這里獲取data。

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data

允許我們?cè)谶@里調(diào)用completionHandler緩存data脏款,或者傳入nil來禁止緩存

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
 willCacheResponse:(NSCachedURLResponse *)proposedResponse
 completionHandler:(void (^)(NSCachedURLResponse * _Nullable cachedResponse))completionHandler{

}

NSURLSessionDownloadDelegate

當(dāng)一個(gè)下載task任務(wù)完成以后围苫,這個(gè)方法會(huì)被調(diào)用。我們可以在這里移動(dòng)或者復(fù)制download的數(shù)據(jù)

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location

獲取下載進(jìn)度

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite

斷點(diǎn)下載撤师。如果下載出錯(cuò)剂府,NSURLSessionDownloadTaskResumeData里面包含重新開始下載的數(shù)據(jù)。

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes

2剃盾、NSURLSession子類

NSURLSessionDataTask

GET請(qǐng)求:

// 快捷方式獲得session對(duì)象
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:@"http://www.daka.com/login?username=daka&pwd=123"];
// 通過URL初始化task,在block內(nèi)部可以直接對(duì)返回的數(shù)據(jù)進(jìn)行處理
NSURLSessionTask *task = [session dataTaskWithURL:url
                               completionHandler:^(NSData *data, NSURLResponse *response, NSError error) {
    NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];

// 啟動(dòng)任務(wù)
[task resume]

POST請(qǐng)求:

NSURL *url = [NSURL URLWithString:@"http://www.daka.com/login"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=daka&pwd=123" dataUsingEncoding:NSUTF8StringEncoding];

NSURLSession *session = [NSURLSession sharedSession];
// 由于要先對(duì)request先行處理,我們通過request初始化task
NSURLSessionTask *task = [session dataTaskWithRequest:request
                                   completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]); }];
[task resume];

NSURLSessionDataDelegate代理方法:

// 使用代理方法需要設(shè)置代理,但是session的delegate屬性是只讀的,要想設(shè)置代理只能通過這種方式創(chuàng)建session
//delegateQueue參數(shù)表示協(xié)議方法將會(huì)在哪個(gè)隊(duì)列(NSOperationQueue)里面執(zhí)行.
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                                      delegate:self
                                                 delegateQueue:[[NSOperationQueue alloc] init]];

// 創(chuàng)建任務(wù)(因?yàn)橐褂么矸椒?就不需要block方式的初始化了)
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.daka.com/login?userName=daka&pwd=123"]]];

// 啟動(dòng)任務(wù)
[task resume];

//對(duì)應(yīng)的代理方法如下:

// 1.接收到服務(wù)器的響應(yīng)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    // 允許處理服務(wù)器的響應(yīng)腺占,才會(huì)繼續(xù)接收服務(wù)器返回的數(shù)據(jù)
    completionHandler(NSURLSessionResponseAllow);
}

// 2.接收到服務(wù)器的數(shù)據(jù)(可能調(diào)用多次)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    // 處理每次接收的數(shù)據(jù)
}

// 3.請(qǐng)求成功或者失敗(如果失敗痒谴,error有值)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    // 請(qǐng)求完成,成功或者失敗的處理
}

NSURLSessionDownloadTask

NSURLSessionDownloadTask詳解

普通下載:

NSURLSessionDownloadTask同樣提供了通過NSURL和NSURLRequest兩種方式來初始化并通過block進(jìn)行回調(diào)的方法.下面以NSURL初始化:

NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:@"http://www.daka.com/resources/image/icon.png"] ;
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
    // location是沙盒中tmp文件夾下的一個(gè)臨時(shí)url,文件下載后會(huì)存到這個(gè)位置,由于tmp中的文件隨時(shí)可能被刪除,所以我們需要自己需要把下載的文件挪到需要的地方
    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
    // 剪切文件
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:path] error:nil];
}];
    // 啟動(dòng)任務(wù)
    [task resume];

downloadTask也提供了配套的代理方法:

// NSURLSessionConfiguration 方式創(chuàng)建 NSURLSession 衰伯,調(diào)用代理方法
NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];  
    self.currentSession = [NSURLSession sessionWithConfiguration:defaultConfig delegate:self delegateQueue:nil];  
    self.currentSession.sessionDescription = kCurrentSession;
    [self.cancellableTask resume];


// 每次寫入調(diào)用(會(huì)調(diào)用多次)  實(shí)現(xiàn)下載進(jìn)度的顯示
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
// 可在這里通過已寫入的長(zhǎng)度和總長(zhǎng)度算出下載進(jìn)度
CGFloat progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite; NSLog(@"%f",progress);
}

// 下載完成調(diào)用
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didFinishDownloadingToURL:(NSURL *)location {
    // location還是一個(gè)臨時(shí)路徑,需要自己挪到需要的路徑(caches下面)
    NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil];
}

// 任務(wù)完成調(diào)用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {

}

斷點(diǎn)下載:

NSURLSession的這種斷點(diǎn)下載只支持應(yīng)用內(nèi)斷點(diǎn),如果程序在下載過程中途關(guān)閉,則不能恢復(fù)下載.

// 使用這種方式取消下載可以得到將來用來恢復(fù)的數(shù)據(jù),保存起來
[self.task cancelByProducingResumeData:^(NSData *resumeData) {
    self.resumeData = resumeData;
}];

// 由于下載失敗導(dǎo)致的下載中斷會(huì)進(jìn)入此協(xié)議方法,也可以得到用來恢復(fù)的數(shù)據(jù)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    // 保存恢復(fù)數(shù)據(jù)
    self.resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData];
}

// 恢復(fù)下載時(shí)接過保存的恢復(fù)數(shù)據(jù)
self.task = [self.session downloadTaskWithResumeData:self.resumeData];
// 啟動(dòng)任務(wù)
[self.task resume];


/* 另外在恢復(fù)下載時(shí),NSURLSessionDownloadDelegate中的以下方法將被調(diào)用,從fileOffset位移處恢復(fù)下載任務(wù) */  
- (void)URLSession:(NSURLSession *)session  
      downloadTask:(NSURLSessionDownloadTask *)downloadTask  
 didResumeAtOffset:(int64_t)fileOffset  
expectedTotalBytes:(int64_t)expectedTotalBytes {  
    NSLog(@"NSURLSessionDownloadDelegate: Resume download at %lld", fileOffset);  
}

后臺(tái)下載:

首先創(chuàng)建一個(gè)后臺(tái)session單例积蔚,這里的Session配置使用后臺(tái)配置模式意鲸,使用backgroundSessinConfiguration:方法配置時(shí)應(yīng)該通過后面的參數(shù)為該后臺(tái)進(jìn)程指定一個(gè)標(biāo)識(shí)符,在有多個(gè)后臺(tái)下載任務(wù)時(shí)這個(gè)標(biāo)識(shí)符就起作用了尽爆。

/* 1怎顾、創(chuàng)建一個(gè)后臺(tái)session單例 */  
- (NSURLSession *)backgroundSession {  
    static NSURLSession *backgroundSess = nil;  
    static dispatch_once_t onceToken;  
    dispatch_once(&onceToken, ^{  
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:kBackgroundSessionID];  
        backgroundSess = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];  
        backgroundSess.sessionDescription = kBackgroundSession;  
    });  
      
    return backgroundSess;  
}


/* 2、在創(chuàng)建后臺(tái)下載任務(wù)時(shí)漱贱,應(yīng)該使用后臺(tái)session創(chuàng)建槐雾,然后resume。*/ 

- (IBAction)backgroundDownload:(id)sender {  
    NSString *imageURLStr = @"http://farm3.staticflickr.com/2831/9823890176_82b4165653_b_d.jpg";  
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURLStr]];  
    self.backgroundTask = [self.backgroundSession downloadTaskWithRequest:request];  
    [self setDownloadButtonsWithEnabled:NO];  
    self.downloadedImageView.image = nil;  
    [self.backgroundTask resume];  
}

/*3  在程序進(jìn)入后臺(tái)后饱亿,如果下載任務(wù)完成蚜退,程序委托中的對(duì)應(yīng)方法將被回調(diào):*/
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {  
    NSLog(@"Application Delegate: Background download task finished");  
      
    // 設(shè)置回調(diào)的完成代碼塊  
    self.backgroundURLSessionCompletionHandler = completionHandler;  
}

/*4闰靴、然后調(diào)用NSURLSessionDownloadDelegate中的方法:(該方法只有下載成功才被調(diào)用)*/ 
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL*)location{
else if (session == self.backgroundSession) {  
    self.backgroundTask = nil;  
    AppDelegate *appDelegate = [AppDelegate sharedDelegate];  
    if (appDelegate.backgroundURLSessionCompletionHandler) {  
        // 執(zhí)行回調(diào)代碼塊  
        void (^handler)() = appDelegate.backgroundURLSessionCompletionHandler;  
        appDelegate.backgroundURLSessionCompletionHandler = nil;  
        handler();  
    }  
}

 
/*5、 完成下載任務(wù)钻注,無論下載成功還是失敗都調(diào)用該方法 */  
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {  
    NSLog(@"NSURLSessionDownloadDelegate: Complete task");  
      
    dispatch_async(dispatch_get_main_queue(), ^{  
        [self setDownloadButtonsWithEnabled:YES];  
    });  
      
    if (error) {  
        NSLog(@"下載失斅烨摇:%@", error);  
        [self setDownloadProgress:0.0];  
        self.downloadedImageView.image = nil;  
    }  
}

NSURLSessionUploadTask

在NSURLSession中,文件上傳方式主要有以下兩種:

NSURLSessionUploadTask *task =
[[NSURLSession sharedSession] uploadTaskWithRequest:request
                                           fromFile:fileName
                                  completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}];


//我們會(huì)使用POST方式進(jìn)行文件上傳,所以較多使用第二種方式
[self.session uploadTaskWithRequest:request
                            fromData:body
                   completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
 NSLog(@"-------%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
 }];


三、NSURLRequest

初始化方法:

//通過這種方式創(chuàng)建的請(qǐng)求對(duì)象 默認(rèn)使用NSURLRequestUseProtocolCachePolicy緩存邏輯 默認(rèn)請(qǐng)求超時(shí)時(shí)限為60s
+ (instancetype)requestWithURL:(NSURL *)URL;

+ (instancetype)requestWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval;

//init方法進(jìn)行對(duì)象的創(chuàng)建 默認(rèn)使用NSURLRequestUseProtocolCachePolicy緩存邏輯 默認(rèn)請(qǐng)求超時(shí)時(shí)限為60s
- (instancetype)initWithURL:(NSURL *)URL;

- (instancetype)initWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval NS_DESIGNATED_INITIALIZER;

常用屬性:

只讀屬性 獲取請(qǐng)求的超時(shí)時(shí)限

@property (readonly) NSTimeInterval timeoutInterval;

主文檔地址 這個(gè)地址用來存放緩存

@property (nullable, readonly, copy) NSURL *mainDocumentURL;

獲取網(wǎng)絡(luò)請(qǐng)求的服務(wù)類型

獲取是否允許使用服務(wù)商蜂窩網(wǎng)絡(luò)

@property (readonly) NSURLRequestNetworkServiceType networkServiceType
@property (readonly) BOOL allowsCellularAccess
@property (nullable, readonly, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;

NSMutableURLRequest

繼承于NSURLRequest

常用屬性:

//設(shè)置請(qǐng)求的URL
@property (nullable, copy) NSURL *URL;
//設(shè)置請(qǐng)求的緩存策略
@property NSURLRequestCachePolicy cachePolicy;
//設(shè)置超時(shí)時(shí)間
@property NSTimeInterval timeoutInterval;
//設(shè)置緩存目錄
@property (nullable, copy) NSURL *mainDocumentURL;
//設(shè)置網(wǎng)絡(luò)服務(wù)類型
@property NSURLRequestNetworkServiceType networkServiceType NS_AVAILABLE(10_7, 4_0);
//設(shè)置是否允許使用服務(wù)商蜂窩網(wǎng)
@property BOOL allowsCellularAccess NS_AVAILABLE(10_8, 6_0);

以下屬性的設(shè)置必須使用NSMutableURLRequest類幅恋,HTTP/HTTPS協(xié)議相關(guān)請(qǐng)求的屬性設(shè)置杏死,如果是NSURLRequest,則只可以讀捆交,不可以修改淑翼。

//設(shè)置HPPT請(qǐng)求方式 默認(rèn)為“GET”
@property (copy) NSString *HTTPMethod;
//通過字典設(shè)置HTTP請(qǐng)求頭的鍵值數(shù)據(jù)
@property (nullable, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;
//設(shè)置http請(qǐng)求頭中的字段值
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(NSString *)field;
//向http請(qǐng)求頭中添加一個(gè)字段
- (void)addValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
//設(shè)置http請(qǐng)求體 用于POST請(qǐng)求
@property (nullable, copy) NSData *HTTPBody;
//設(shè)置http請(qǐng)求體的輸入流
@property (nullable, retain) NSInputStream *HTTPBodyStream;
//設(shè)置發(fā)送請(qǐng)求時(shí)是否發(fā)送cookie數(shù)據(jù)
@property BOOL HTTPShouldHandleCookies;
//管道線技術(shù),設(shè)置請(qǐng)求時(shí)是否按順序收發(fā) 默認(rèn)禁用 在某些服務(wù)器中設(shè)為YES可以提高網(wǎng)絡(luò)性能
@property BOOL HTTPShouldUsePipelining;

四、NSURLResponse

初始化方法不經(jīng)常用到品追,略玄括。

常用的屬性:

//服務(wù)的URL地址
@property (nullable, readonly, copy) NSURL *URL;
//返回的數(shù)據(jù)信息(純文本,視頻肉瓦,語音遭京,超文本等)
@property (nullable, readonly, copy) NSString *MIMEType;
//將要返回的數(shù)據(jù)的大小
@property (readonly) long long expectedContentLength;
//編碼的名稱
@property (nullable, readonly, copy) NSString *textEncodingName;
//獲取服務(wù)器的文件的名稱
@property (nullable, readonly, copy) NSString *suggestedFilename;

NSHTTPURLResponse

繼承于NSURLResponse

SInteger statusCode:返回的狀態(tài)碼,成功泞莉,失敗哪雕,無效請(qǐng)求等,(200鲫趁,404斯嚎,400等)
NSDictionary* allHeaderFields:返回?cái)?shù)據(jù)的頭部信息,key-value格式

@property (readonly) NSInteger statusCode;
@property (readonly, copy) NSDictionary *allHeaderFields;


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挨厚,一起剝皮案震驚了整個(gè)濱河市堡僻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌幽崩,老刑警劉巖苦始,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寞钥,死亡現(xiàn)場(chǎng)離奇詭異慌申,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)理郑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門蹄溉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人您炉,你說我怎么就攤上這事柒爵。” “怎么了赚爵?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵棉胀,是天一觀的道長(zhǎng)法瑟。 經(jīng)常有香客問我,道長(zhǎng)唁奢,這世上最難降的妖魔是什么霎挟? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮麻掸,結(jié)果婚禮上酥夭,老公的妹妹穿的比我還像新娘。我一直安慰自己脊奋,他們只是感情好熬北,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著诚隙,像睡著了一般讶隐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上久又,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天整份,我揣著相機(jī)與錄音,去河邊找鬼籽孙。 笑死烈评,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的犯建。 我是一名探鬼主播讲冠,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼适瓦!你這毒婦竟也來了竿开?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤玻熙,失蹤者是張志新(化名)和其女友劉穎否彩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗦随,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡列荔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了枚尼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贴浙。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖署恍,靈堂內(nèi)的尸體忽然破棺而出崎溃,到底是詐尸還是另有隱情,我是刑警寧澤盯质,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布袁串,位于F島的核電站概而,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏囱修。R本人自食惡果不足惜到腥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蔚袍。 院中可真熱鬧乡范,春花似錦、人聲如沸啤咽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宇整。三九已至瓶佳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鳞青,已是汗流浹背霸饲。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留臂拓,地道東北人厚脉。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像胶惰,于是被迫代替她去往敵國和親傻工。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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