学习 NSURLSession

前言

现如今的移动应用开发,网络模块已经成为标配。说起网络请求很多人会提到AFNetworking, 苹果本身也提供了NSURLConnection组件,但其操作起来有许多不便,这也使得大家更愿意使用第三方库的解决方案,正是因为这一点,苹果对它进行重构,在 2013 的 WWDC 上推出了继任者:NSURLSession ,并在 iOS 9.0 之后直接宣布弃用NSRULConnection

NSURLSession

NSURLConnection 一样,NSURLSession 指的也不仅是同名类 NSURLSession,还包括一系列相互关联的类。NSURLSession 包括了与之前相同的组件,NSURLRequestNSURLCache,但是把 NSURLConnection 替换成了 NSURLSessionNSURLSessionConfiguration 以及 NSURLSessionTask 的 3 个子类:NSURLSessionDataTaskNSURLSessionUploadTaskNSURLSessionDownloadTask

NSURLConnection 相比,NSURLsession 最直接的改进就是可以配置每个 session 的缓存,协议,cookie,以及证书策略(credential policy),甚至跨程序共享这些信息。这将允许程序和网络基础框架之间相互独立,不会发生干扰。每个 NSURLSession 对象都由一个 NSURLSessionConfiguration 对象来进行初始化,后者指定了刚才提到的那些策略以及一些用来增强移动设备上性能的新选项。

NSURLSession 中另一大块就是 session task。它负责处理数据的加载以及文件和数据在客户端与服务端之间的上传和下载。NSURLSessionTaskNSURLConnection 最大的相似之处在于它也负责数据的加载,最大的不同之处在于所有的 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 之间的 cachecookie 是共享的。

Task 可以翻译为任务,那么在和网络请求相关的任
务中,我们可以理解为:数据请求任务、下载任务、上传任务等。

学习 NSURLSession_第1张图片
NSURLSessionTask.png

当一个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,可以使用缓存的 CacheCookie,证书(credential

  • 2 及时模式(+ephemeralSessionConfiguration): 临时session 配置,与默认配置相比,这个配置不会使用缓存的 CacheCookie,证书,只会存在内存里,所以当程序退出时,所有的数据都会消失,这对于实现像秘密浏览这种功能来说是很理想的

  • 3 后台模式(+backgroundSessionConfiguration):它会创建一个后台 session。后台 session 不同于常规的,普通的session,它甚至可以在应用程序挂起,退出或者崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程(daemon)提供上下文

除了这三种预设的模式之外NSURLSessionConfiguration还可以进行很多的配置。timeoutIntervalForRequesttimeoutIntervalForResource可以控制网络操作的超时时间。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)中分离出来

你可能感兴趣的:(学习 NSURLSession)