在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聚蝶。
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
block形式創(chuàng)建并執(zhí)行block,和delegate一樣排作,Block也有dataTask系列牵啦、downloadTask系列、uploadTask系列
獲取 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;
@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
在應(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)系:
從繼承關(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同樣提供了通過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;