NSURLSession進(jìn)行網(wǎng)絡(luò)請(qǐng)求
前言:
蘋(píng)果的網(wǎng)絡(luò)請(qǐng)求有NSURLConnection和NSURLSession,而NSURLConnection在iOS9被宣布棄用。AFNetworking對(duì)NSURLSession進(jìn)行了封裝,另外,一般公司里用的網(wǎng)絡(luò)庫(kù),也是在NSURLSession基礎(chǔ)上進(jìn)行封裝,使得符合自己業(yè)務(wù)的開(kāi)發(fā)需求,所以,我們很有必要對(duì)NSURLSession的使用進(jìn)行了解。本文對(duì)NSURLSession進(jìn)行簡(jiǎn)單的介紹。
正文:
一、NSURLSession簡(jiǎn)介
使用NSURLSession,共分兩步:
- 第一步 通過(guò)NSURLSession的實(shí)例創(chuàng)建task
- 第二步 執(zhí)行task
NSURLSessionTask的繼承關(guān)系如下圖所示:

二、NSURLSessionTask的使用舉例(分6種情況)
(1) NSURLSessionTask進(jìn)行簡(jiǎn)單的Get請(qǐng)求
NSURLSession *session = [NSURLSession sharedSession]; NSURL *url = [NSURL URLWithString:@"https://xxx.yy.zz/check"]; NSURLSessionTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){ if(!error){ NSDictionary *responseDic = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; NSString *status = responseDic[@"status"]; if ([status isKindOfClass:[NSString class]] && [status isEqualToString:@"1"]) { self.needAutoCode = YES ; [self.tableView reloadData]; } }else { NSLog(@"%@", error); } }]; [task resume];
(2) NSURLSessionTask進(jìn)行簡(jiǎn)單的Post請(qǐng)求
NSURL *url = [NSURL URLWithString:@"https://xxx.xxx.xx/accessToken"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"POST"; NSString *param = [NSString stringWithFormat:@"code=%@&client_secret=3QmKoBbku7&client_id=apollo&grant_type=code",self.serviceTicket]; request.HTTPBody = [param dataUsingEncoding:NSUTF8StringEncoding]; NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){ if((!error) && ([(NSHTTPURLResponse *)response statusCode] == 200)) { NSError *error0 = nil; NSDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error0]; if(!error0){ NSString *refreshToken = jsonDic[@"refresh_token"]; NSString *accessToken = jsonDic[@"access_token"]; //此處一些業(yè)務(wù)處理 }else{ NSLog(@"%@",error0.nv_message); } }else{ NSLog(@"%@",error.nv_message); } }]; [task resume];
(3)NSURLSessionDownloadTask進(jìn)行簡(jiǎn)單的文件下載
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];
(4)斷點(diǎn)下載
// 使用這種方式取消下載可以得到將來(lái)用來(lái)恢復(fù)的數(shù)據(jù),保存起來(lái) [self.task cancelByProducingResumeData:^(NSData *resumeData) { self.resumeData = resumeData; }]; // 由于下載失敗導(dǎo)致的下載中斷會(huì)進(jìn)入此協(xié)議方法,也可以得到用來(lái)恢復(fù)的數(shù)據(jù) - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{ // 保存恢復(fù)數(shù)據(jù) self.resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData]; } // 恢復(fù)下載時(shí)接過(guò)保存的恢復(fù)數(shù)據(jù) self.task = [self.session downloadTaskWithResumeData:self.resumeData]; // 啟動(dòng)任務(wù) [self.task resume];
(5)文件上傳
NSURLSessionUploadTask *uploadtask1 = [self.session uploadTaskWithRequest:request fromFile:fileName completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {}]; //或者,出于安全性考慮,通常使用POST方式進(jìn)行文件上傳 NSURLSessionUploadTask *uploadtask2 = [self.session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"-------%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);}];
(6) 使用代理NSURLSessionDataDelegate
NSURLSession提供了block方式處理返回?cái)?shù)據(jù)的簡(jiǎn)便方式,但如果想要在接收數(shù)據(jù)過(guò)程中做進(jìn)一步的處理,仍然可以調(diào)用相關(guān)的協(xié)議方法.NSURLSession的代理方法分為接收響應(yīng)、接收數(shù)據(jù)、請(qǐng)求完成幾個(gè)階段。
NSURLSessionDelegate的繼承關(guān)系如下圖所示:

//使用代理方法需要設(shè)置代理,但是session的delegate屬性是只讀的,要想設(shè)置代理只能通過(guò)這種方式創(chuàng)建session self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil]; NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@""]]; // 創(chuàng)建任務(wù)(因?yàn)橐褂么矸椒?就不需要block方式的初始化了)和啟動(dòng)任務(wù) //(1)一般請(qǐng)求任務(wù) NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request]; [task resume]; //(2)上傳任務(wù) NSURLSessionUploadTask *uploadtask = [self.session uploadTaskWithRequest:request fromData:request.HTTPBody]; [uploadtask resume]; //(3)下載任務(wù) NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request]; [downloadTask resume];
//對(duì)應(yīng)的代理方法如下:
# pragma mark NSURLSessionTaskDelegate // 請(qǐng)求成功或者失敗(如果失敗,error有值) - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { // 請(qǐng)求完成,成功或者失敗的處理 if (self.completion) { NSData *data = nil; if (self.receivedData) { data = [self.receivedData copy]; self.receivedData = nil; } self.completion(self.cmd, task.response, data, error,error); } [self.session invalidateAndCancel]; } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { if (self.uploadBlock) { int64_t totalUnitCount = totalBytesExpectedToSend; if(totalUnitCount == NSURLSessionTransferSizeUnknown) { NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"]; if(contentLength) { totalUnitCount = (int64_t) [contentLength longLongValue]; } } NSInteger totalBytesSentInt = [[NSNumber numberWithLongLong:totalBytesSent] integerValue]; NSInteger totalUnitCountInt = [[NSNumber numberWithLongLong:totalUnitCount] integerValue]; self.uploadBlock(totalBytesSentInt,totalUnitCountInt); } } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * __nullable))completionHandler{ //禁止302 301重定向 completionHandler(nil); } # pragma mark NSURLSessionDataDelegate // 接收到服務(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); } // 接收到服務(wù)器的數(shù)據(jù)(可能調(diào)用多次) - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { // 處理每次接收的數(shù)據(jù) [self.receivedData appendData:data]; } # pragma mark NSURLSessionDownloadDelegate // 每次寫(xiě)入調(diào)用(會(huì)調(diào)用多次) - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { if (self.downloadBlock) { NSInteger bytesWrittenInt = [[NSNumber numberWithLongLong:totalBytesWritten] integerValue]; NSInteger totalBytesExpectedToWriteInt = [[NSNumber numberWithLongLong:totalBytesExpectedToWrite] integerValue]; self.downloadBlock(bytesWrittenInt,totalBytesExpectedToWriteInt); } //另外, 可在這里通過(guò)已寫(xiě)入的長(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 { self.receivedData = [[NSData dataWithContentsOfURL:location.filePathURL] mutableCopy]; }
三、其他
task擁有下面三個(gè)方法:
- (void)suspend; - (void)resume; - (void)cancel;
suspend可以讓當(dāng)前的任務(wù)暫停;
resume方法不僅可以啟動(dòng)任務(wù),還可以喚醒suspend狀態(tài)的任務(wù);
cancel方法可以取消當(dāng)前的任務(wù),你也可以向處于suspend狀態(tài)的任務(wù)發(fā)送cancel消息,任務(wù)如果被取消便不能再恢復(fù)到之前的狀態(tài).
NSURLSessionConfiguration配置有三種類型:
+ (NSURLSessionConfiguration *)defaultSessionConfiguration; + (NSURLSessionConfiguration *)ephemeralSessionConfiguration; + (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier
默認(rèn)的配置會(huì)將緩存存儲(chǔ)在磁盤(pán)上,第二種瞬時(shí)會(huì)話模式不會(huì)創(chuàng)建持久性存儲(chǔ)的緩存,第三種后臺(tái)會(huì)話模式允許程序在后臺(tái)進(jìn)行上傳下載工作.
配置可以設(shè)置session的一些配置信息,如下舉例:
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; // 超時(shí)時(shí)間 config.timeoutIntervalForRequest = 10; // 是否允許使用蜂窩網(wǎng)絡(luò)(后臺(tái)傳輸不適用) config.allowsCellularAccess = YES; // 還有很多可以設(shè)置的屬性
特別說(shuō)明,本文是對(duì)簡(jiǎn)書(shū)《使用NSURLSession》的學(xué)習(xí)與記錄,在使用方面做了一些調(diào)研和實(shí)驗(yàn)。

浙公網(wǎng)安備 33010602011771號(hào)