导语
因为苹果在IOS9 之后已经放弃了NSURLConnection,所以现在开发中,除了AFN框架,一般使用的就是iOS 107之后推出的NSURLSession(用于替代 NSURLConnection, 针对下载/上传等复杂的网络操作提供了专门的解决方案!)。所以今天我们聊聊NSURLSession。
核心类
1.NSURLSession:会话对象。
2.NSURLSessionConfiguration:创建会话对象的相关配置。
3.NSURLSessionTask:会话任务的基类。
4.NSURLSessionDataTask:网络通信,向服务器发送数据的同时,接受服务器反馈的信息。
5.NSURLSessionUploadTask:上传文件。
6.NSURLSessionDownloadTask:下载文件。
协议
NSURLSessionDelegate:会话的协议。
NSURLSessionTaskDelegate:任务的协议。
NSURLSessionDataDelegate:监听NSURLSessionDataTask和NSURLSessionUploadTask的工作状态。
NSURLSessionDownloadDelegate:监听NSURLSessionDownloadTask的工作状态。
NSURLSession的基本使用
//创建URL
NSURL * url = [NSURL URLWithString:@"http://192.168.1.200/login.php?username=haha&password=123"];
//创建请求
// NSURLRequest * request = [NSURLRequest requestWithURL:url];
//创建Session
NSURLSession * session = [NSURLSession sharedSession];
//创建任务(任务可以通过NSURL和NSURLRequest两种方式来初始化并通过block进行回调的方法)
NSURLSessionDataTask * task = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
//NSURLSessionDataTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
// }];
//开启网络任务
[task resume];
NSURLConfiguration笔记
NSURLSessionConfiguration对象用于初始化NSURLSession对象。
- (NSURLSessionConfiguration *)defaultSessionConfiguration;
//返回标准配置,这实际上与NSURLConnection的网络协议栈是一样的,具有相同的共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage。 - (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
//返回一个预设配置,没有持久性存储的缓存,Cookie或证书。这对于实现像"秘密浏览"功能的功能来说,是很理想的。 - (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);
//独特之处在于,它会创建一个后台会话。后台会话不同于常规的,普通的会话,它甚至可以在应用程序挂起,退出,
崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程提供上下文。
-(NSURLSession *)session
{
if (_session == nil) {
//创建NSURLSessionConfiguration
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
//设置请求超时为10秒钟
config.timeoutIntervalForRequest = 10;
//在蜂窝网络情况下是否继续请求(上传或下载)
config.allowsCellularAccess = NO;
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
NSURLSessionTask的各种子类使用
NSURLSessionTask 有两个子类
- NSURLSessionDataTask,可以用来处理一般的网络请求,如 GET | POST 请求等
- NSURLSessionDataTask 有一个子类为 NSURLSessionUploadTask,用于处理上传请求的时候有优势
- NSURLSessionDownloadTask,主要用于处理下载请求,有很大的优势
NSURLSessionDataTask ———处理一般的网络请求
一:GET请求
//确定请求路径
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];
//发送GET请求的第二种方法
/**
注意:该block是在子线程中调用的,如果拿到数据之后要做一些UI刷新操作,那么需要回到主线程刷新
第一个参数:需要发送的请求对象
block:当请求结束拿到服务器响应的数据时调用block
block-NSData:该请求的响应体
block-NSURLResponse:存放本次请求的响应信息,响应头,真实类型为NSHTTPURLResponse
block-NSErroe:请求错误信息
*/
//- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
二:POST请求
//确定请求路径
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. URL
NSString *urlString = @"http://192.168.26.201/0805空缺数据 2.xls.zip";
// 添加 % 转义
urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlString];
// NSURLSession,sharedSession是一个全局共享的单例,苹果为了方便程序员使用简单的网络任务
// NSURLSession *session = [NSURLSession sharedSession];
// 2. 由sharedSession发起网络会话任务
[[[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
NSLog(@"%@", [NSBundle mainBundle].bundlePath);
NSLog(@"%@", location);
}] resume];
四:NSURLSessionDataDelegate代理方法
- 使用NSURLSession发送GET请求(代理方式)
NSURLSession对象需要自定义
遵守协议NSURLSessionDataDelegate,实现相应的代理方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// 获得NSURLSession对象
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
// 创建任务
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com/login?username=123&pwd=4324"]]];
// 启动任务
[task resume];
}
#pragma mark - <NSURLSessionDataDelegate>
// 1.接收到服务器的响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
// 允许处理服务器的响应,才会继续接收服务器返回的数据
completionHandler(NSURLSessionResponseAllow);
}
// 2.接收到服务器的数据(可能会被调用多次)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
NSLog(@"%s", __func__);
}
// 3.请求成功或者失败(如果失败,error有值)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"%s", __func__);
}
使用NSURLSession发送POST请求(代理方式),实现方式和get请求一样,只需要设置请求头
NSURLSessionUploadTask———文件上传
// 设置请求头
request.HTTPMethod = @"POST";
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", XMGBoundary] forHTTPHeaderField:@"Content-Type"];
// 设置请求体
NSMutableData *body = [NSMutableData data];
// 文件参数
// 分割线
[body appendData:XMGEncode(@"--")];
[body appendData:XMGEncode(XMGBoundary)];
[body appendData:XMGNewLine];
.....
// 注意这里通过设置请求体 = data完成文件上传,官方说这样做会被忽略
// 就是说, 如果利用NSURLSessionUploadTask上传文件, 那么请求体必须写在fromData参数中, 不能设置在request中. 否则设置在request中会被忽略
/*
The body stream and body data in this request object are ignored.
*/
// request.HTTPBody = data; ❌
// 1.创建session
NSURLSession *session = [NSURLSession sharedSession];
// 2.根据session创建Task
//注意这里的data是文件参数和非文件参数的拼接二进制❤️
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData: body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
// 3.执行Task
[task resume];
// 注意:不要使用这个方法. fromFile方法是用于PUT请求上传文件的
// 而我们的服务器只支持POST请求上传文件
[session uploadTaskWithRequest:request fromFile:fileUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {}];❌
文件上传的监听
// 1.创建session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 2.根据session创建Task
// 该方法中有回调函数,会影响代理方法调用🈲
// NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { }];
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body];
// 3.执行Task
[task resume];
// =================代理方法====================
#pragma mark - NSURLSessionTaskDelegate
// 上传过程中调用
/*
bytesSent:当前这一次上传的数据大小;
totalBytesSent:总共上传数据大小
totalBytesExpectedToSend:需要上传的文件大小
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
NSLog(@"didSendBodyData");
NSLog(@"%f",1.0 * totalBytesSent/totalBytesExpectedToSend);
}
// 请求完毕时调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError");
}
NSURLSessionDownloadTask ———文件下载任务
注意:
1.默认情况下,使用NSURLSessionDownloadTask时,系统已经帮我们实现边下载边存储.防止内存暴增.
2.而我们只需要把下载的资源从不安全的tmp文件夹挪到caches文件夹.
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];
- NSURLSessionDownloadDelegate代理方法
// 接受到下载数据时调用[在该方法监听文件下载的进度],此方法会被调用多次
/*
totalBytesWritten:已经写入到文件中的数据大小
totalBytesExpectedToWrite:目前文件的总大小
bytesWritten:本次下载的文件数据大小
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
// 获取文件下载进度
CGFloat progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite; NSLog(@"%f",progress);
}
// 下载完成调用
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location {
// location还是一个临时路径,需要自己挪到需要的路径(caches下面)
NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil];
}
/*
恢复下载的时候调用该方法
fileOffset:恢复之后,要从文件的什么地方开发下载
expectedTotalBytes:该文件数据的总大小
*/
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{
}
// 任务完成调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
}
- dataTask 和 downloadTask 下载对比
- NSURLSessionDataTask
下载文件可以实现离线断点下载,但是代码相对复杂 - NSURLSessionDownloadTask
下载文件可以实现断点下载,但不能离线断点下载
内部已经完成了边接收数据边写入沙盒的操作
解决了下载大文件时的内存飙升问题
- NSURLSessionDataTask
NSURLSession优点
后台上传和下载:只需在创建NSURLSession的时候配置一个选项,就能得到后台网络的所有好处。这样可以延长电池寿命,并且还支持UIKit的多task,在进程间使用相同的委托模型。
能够暂停和恢复网络操作:使用NSURLSession API能够暂停,停止,恢复所有的网络任务,再也完全不需要子类化NSOperation.可配置的容器:对于NSURLSession里面的requests来说,每个NSURLSession都是可配置的容器。举个例来说,假如你需要设置HTTP header选项,你只用做一次,session里面的每个request就会有同样的配置。
提高认证处理:认证是在一个指定的连接基础上完成的。在使用NSURLConnection时,如果发出一个访问,会返回一个任意的request。此时,你就不能确切的知道哪个request收到了访问。而在NSURLSession中,就能用代理处理认证。
丰富的代理模式:在处理认证的时候,NSURLConnection有一些基于异步的block方法,但是它的代理方法就不能处理认证,不管请求是成功或是失败。在NSURLSession中,可以混合使用代理和block方法处理认证。
上传和下载通过文件系统:它鼓励将数据(文件内容)从元数据(URL和settings)中分离出来。