前言
现如今的移动应用开发,网络模块已经成为标配。说起网络请求很多人会提到AFNetworking, 苹果本身也提供了NSURLConnection
组件,但其操作起来有许多不便,这也使得大家更愿意使用第三方库的解决方案,正是因为这一点,苹果对它进行重构,在 2013 的 WWDC 上推出了继任者:NSURLSession
,并在 iOS 9.0 之后直接宣布弃用NSRULConnection
。
NSURLSession
和 NSURLConnection
一样,NSURLSession
指的也不仅是同名类 NSURLSession
,还包括一系列相互关联的类。NSURLSession
包括了与之前相同的组件,NSURLRequest
与 NSURLCache
,但是把 NSURLConnection
替换成了 NSURLSession
、NSURLSessionConfiguration
以及 NSURLSessionTask
的 3 个子类:NSURLSessionDataTask
,NSURLSessionUploadTask
,NSURLSessionDownloadTask
。
与 NSURLConnection
相比,NSURLsession
最直接的改进就是可以配置每个 session
的缓存,协议,cookie
,以及证书策略(credential policy
),甚至跨程序共享这些信息。这将允许程序和网络基础框架之间相互独立,不会发生干扰。每个 NSURLSession
对象都由一个 NSURLSessionConfiguration
对象来进行初始化,后者指定了刚才提到的那些策略以及一些用来增强移动设备上性能的新选项。
NSURLSession
中另一大块就是 session task
。它负责处理数据的加载以及文件和数据在客户端与服务端之间的上传和下载。NSURLSessionTask
与 NSURLConnection
最大的相似之处在于它也负责数据的加载,最大的不同之处在于所有的 task
共享其创造者 NSURLSession
这一公共委托者(common delegate
)。
- 默认获取 Session 对象
/*
* 用于基本的网络请求,可以几行代码就获取 URL 的内容,使用简单
* 无法不断的获取服务器返回的数据
* 无法修改默认的连接行为
* 身份验证的能力有限
* 任务在后台时无法上传和下载
*/
+ (NSURLSession *)sharedSession;
- 自定义 Session 对象
// 不用代理 根据 NSURLSessionConfiguration 创建对应配置的 seesion
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
// 用代理的 根据 NSURLSessionConfiguration 创建对应配置的 seesion,并且可以指定 seesion 的委托和委托所处的队列
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id )delegate delegateQueue:(nullable NSOperationQueue *)queue;
// 自定义 Session 有三种 NSURLSessionConfiguration 配置
// 和默认 Session 对象类似,但可以通过设置代理不断获取数据
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
// 和 default sessions 类似,但无法写入 caches, cookies 和 credentials 到磁盘
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
// 当任务在后台时仍可以上传和下载数据
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);
NSURLSessionTask
NSURLSession
本身是不会进行请求的,而是通过创建 task
的形式进行网络请求,同一个 NSURLSession
可以创建多个 task
,并且这些 task
之间的 cache
和 cookie
是共享的。
Task
可以翻译为任务,那么在和网络请求相关的任
务中,我们可以理解为:数据请求任务、下载任务、上传任务等。
当一个NSURLSessionDataTask
完成时,它会带有相关联的数据,而一个NSURLSessionDownloadTask
任务结束时,它会带回已下载文件的一个临时的文件路径。因为一般来说,服务端对于一个上传任务的响应也会有相关数据返回,所以NSURLSessionUploadTask
继承自NSURLSessionDataTask
。所有的 task
都是可以取消,暂停或者恢复的。当一个 download task
取消时,可以通过选项来创建一个恢复数据(resume data),然后可以传递给下一次新创建的 download task
,以便继续之前的下载。
NSURLSessionDataTask:
使用这个 task 来调用 HTTP GET 方式请求,从服务器获取数据到内存。返回的数据格式是 NSData,可根据需要自行转换成 XML、JSON 等数据格式
- 简单 Get 请求
如果请求的数据简单并且不需要对获取的数据进行复杂操作,我们使用 Block 解析返回的数据即可。
/**
* 简单 GET 请求
*/
- (void)getWithsharedSession
{
// 获取默认 Session
NSURLSession *session = [NSURLSession sharedSession];
// 创建 URL
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=1234&pwd=4321"];
// 创建任务 task
NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 获取数据后解析并输出
NSLog(@"%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
// 启动任务
[task resume];
}
- 简单 Post 请求
/**
* 简单 Post 请求,POST 和 GET 请求在于对 request 的处理不同,其余和 GET 相同
*/
- (void)postWithSharedSession
{
// 获取默认 Session
NSURLSession *session = [NSURLSession sharedSession];
// 创建 URL
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
// 创建 request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 请求方法
request.HTTPMethod = @"POST";
// 请求体
request.HTTPBody = [@"username=1234&pwd=4321" dataUsingEncoding:NSUTF8StringEncoding];
// 创建任务 task
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 获取数据后解析并输出
NSLog(@"%@",[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
// 启动任务
[task resume];
}
NSURLSessionDataDelegate 代理方法
NSURLSession 提供了 block 的方式处理返回的数据,但是如果我们想要在接收数据的过程中处理数据,我们可以使用 NSURLSessionDataDelegate 的代理方法,分为接收响应、接收数据和请求完成三个阶段。
- (void)sessionDataDelegate
{
// 创建带有代理方法的自定义 session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 创建任务
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/login?username=1234&pwd=4321"]]];
// 启动任务
[task resume];
}
// 1. 接受到服务器的响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
NSLog(@"任务完成");
// 必须设置对响应进行允许处理才会执行后面两个操作。
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(@"SessionTask %s",__func__);
}
**NSURLSessionUploadTask: **
使用这个 task 来上传磁盘文件到 web 服务器,典型地通过 HTTP POST 或者 PUT 方式
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;
NSURLSessionDownloadTask:
使用这个 task 来从远程服务器下载文件到 tmp 临时文件地址,也可以手动更改地址
/**
* NSURLSessionDownloadTask 下载任务
*/
- (void)downLoad
{
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"] ;
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
// location 是沙盒中 tmp 文件夹下的一个临时 url,文件下载后会存到这个位置,由于 tmp 中的文件随时可能被删除,所以我们需要自己需要把下载的文件挪到 Caches 文件夹中
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
// 剪切文件
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:path] error:nil];
}];
// 启动任务
[task resume];
}
** NSURLSessionDownloadTask 代理方法下载**
downloadTask 同样提供对应的代理方法。
/**
* NSURLSessionDownloadTask 代理
*/
- (void)sessionDownloadTaskDelegate
{
// 创建带有代理方法的自定义 session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 创建任务
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]];
// 启动任务
[task resume];
}
/**
* 写入临时文件时调用
* @param bytesWritten 本次写入大小
* @param totalBytesWritten 已写入文件大小
* @param totalBytesExpectedToWrite 请求的总文件的大小
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
CGFloat progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite;
NSLog(@"downloadTask %f",progress);
}
// 2. 下载完成调用
- (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];
NSLog(@"downloadTask 移动文件路径");
}
// 3. 请求成功或者失败(如果失败,error有值)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
// 请求完成,成功或者失败的处理
NSLog(@"SessionTask %s",__func__);
}
NSURLSessionConfiguration:
NSURLSessionConfiguration
对象用于对NSURLSession
对象进行初始化。从指定可用网络,到 cookie
,安全性,缓存策略,再到使用自定义协议,启动事件的设置,以及用于移动设备优化的几个新属性,你会发现使用NSURLSessionConfiguration
可以找到几乎任何你想要进行配置的选项。其有三个类工厂方法:
1 默认模式(
+defaultSessionConfiguration
):返回一个标准的configuration
,工作模式类似于NSURLConnection
,可以使用缓存的Cache
,Cookie
,证书(credential
)2 及时模式(
+ephemeralSessionConfiguration
): 临时session
配置,与默认配置相比,这个配置不会使用缓存的Cache
,Cookie
,证书,只会存在内存里,所以当程序退出时,所有的数据都会消失,这对于实现像秘密浏览这种功能来说是很理想的3 后台模式(
+backgroundSessionConfiguration
):它会创建一个后台session
。后台session
不同于常规的,普通的session
,它甚至可以在应用程序挂起,退出或者崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程(daemon
)提供上下文
除了这三种预设的模式之外NSURLSessionConfiguration
还可以进行很多的配置。timeoutIntervalForRequest
和timeoutIntervalForResource
可以控制网络操作的超时时间。allowsCellularAccess
属性可以控制是否允许使用无线网络。HTTPAdditionalHeaders
可以指定 HTTP 请求头。
NSURLSessionConfiguration
几乎可以完成网络操作的大多数配置功能,并且这些配置都绑定到当前的 Session
中,我们一旦用配置好的NSURLSessionConfiguration
初始化NSURLSession
实例后,就不能修改这个NSURLSession
相关的配置了。所以,一切的配置操作都放在初始化NSURLSession
之前。
**NSURLSessionStreamTask: **
使用这个 task 来执行异步的读和写,另外也添加NSURLSessionStreamDelegate代理方法,用于跟踪整个TCP连接的状态过程.
NSURLConnection 与 NSURLSession 对比
后台上传和下载:只需在创建 NSURLSession
的时候配置一个选项,就能得到后台网络的所有好处。这样可以延长电池寿命,并且还支持 UIKit
的多 task
,在进程间使用相同的委托模型
能够暂停和恢复网络操作:使用 NSURLSession API
能够暂停,停止,恢复所有的网络任务,再也完全不需要子类化NSOperation
可配置的容器:对于NSURLSession
里面的requests
来说,每个NSURLSession
都是可配置的容器。举个例来说,假如你需要设置 HTTP header
选项,你只用做一次,session
里面的每个 request
就会有同样的配置
提高认证处理:认证是在一个指定的连接基础上完成的。在使用 NSURLConnection
时,如果发出一个访问,会返回一个任意的 request
。此时,你就不能确切的知道哪个request
收到了访问。而在 NSURLSession
中,就能用代理处理认证
丰富的代理模式:在处理认证的时候,NSURLConnection
有一些基于异步的block
方法,但是它的代理方法就不能处理认证,不管请求是成功或是失败。在 NSURLSession
中,可以混合使用代理和 block
方法处理认证
上传和下载通过文件系统:它鼓励将数据(文件内容)从元数据(URL 和 settings)中分离出来