iOS | NSURLSession 介绍与使用

简介

NSURLSession 是苹果公司在2013的 WWDC大会上随 iOS7 一起发布的,是 NSURLConnection 的继任者。 NSURLConnection在iOS9 之后也被宣布弃用。目前 AFNetworking、SDWebImage 等知名类库也都更新使用了 NSURLSession 。

NSURLSession的优势:

  • 针对不同的网络请求任务提供了专门的解决方案;
  • 支持任务取消、暂停、恢复、支持应用程序后台下载;
  • 安全身份验证方案;
  • 下载操作内存优化;
  • 更灵活请求的配置。

使用

首先我们看下NSURLSession最简单的使用,通过下面代码就可以实现了基本的网络请求了。

    NSURL *url = [NSURL URLWithString:@"http://....."];
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *task = [session dataTaskWithURL:url  completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
       // handle  response
    }];
    [task resume];

所有的NSURLSession 网络请求基本遵循以下几个步骤:

  1. 获取NSURLSession对象
  2. 通过session对象创建 task 任务
  3. 执行 task(task 默认是挂起的,通过resume执行)

NSURLSession

NSURLSession继承NSObject,提供了3种会话模式,主要通过 NSURLSessionConfiguration来进行初始化,同时还可以设置 dalegate 来进行下载过程监听

+ (NSURLSession *)sharedSession; //共享的会话,该会话使用全局的Cache,Cookie和证书,无法进行 Configuration配置
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration; // 通过 NSURLSessionConfiguration初始化
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;  // 通过 NSURLSessionConfiguration、 delegate 、队列初始化

接下来我们看下NSURLSessionConfiguration的3种配置,他们是和NSURLSession的会话模式一一对应的

@property (class, readonly, strong) NSURLSessionConfiguration *defaultSessionConfiguration;
@property (class, readonly, strong) NSURLSessionConfiguration *ephemeralSessionConfiguration;
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
  • defaultSessionConfiguration 默认配置使用的是持久化的硬盘缓存,存储证书到用户钥匙链。存储cookie到shareCookie。
  • ephemeralSessionConfiguration 这个配置中不会对缓存,Cookie 和证书进行持久性的存储。
  • + backgroundSessionConfigurationWithIdentifier:(NSString *)identifier 它会返回一个后台 session。可以在应用程序挂起,退出或者崩溃的情况下运行上传和下载任务。

另外: NSURLSessionConfiguration是提供了很多属性,从 指定可用网络,到 cookie,安全性,缓存策略,再到使用自定义协议,启动事件的设置,以及用于移动设备优化的几个新属性,可以找到几乎任何你想要进行配置的选项。

了解了 NSURLSession的模式以及配置,接下来我们看一下 NSURLSessionTask

NSURLSessionTask

NSURLsessionTask 是一个抽象类,其下有 3 个实体子类可以直使用用:NSURLSessionDataTaskNSURLSessionUploadTaskNSURLSessionDownloadTask。这 3 个子类封装了程序三个最基本的网络任务:获取数据,比如 JSON 或者 XML,上传文件和下载文件。

QQ140736@2x.png
  • NSURLSessionDataTask 可以用来处理一般的网络请求
  • NSURLSessionDownloadTask 主要用于处理下载请求。
  • NSURLSessionUploadTask 用于处理上传请求。

当一个 NSURLSessionDataTask 完成时,它会带有相关联的数据,而一个 NSURLSessionDownloadTask 任务结束时,它会带回已下载文件的一个临时的文件路径。因为一般来说,服务端对于一个上传任务的响应也会有相关数据返回,所以 NSURLSessionUploadTask 继承自 NSURLSessionDataTask

所有的task 都是可以取消,暂停或者恢复的。当一个 download task 取消时,可以通过选项来创建一个恢复数据(resume data),然后可以传递给下一次新创建的 download task,以便继续之前的下载。

不同于直接使用alloc-init 初始化方法,task 是由一个 NSURLSession 创建的。每个 task 的构造方法都对应有或者没有 completionHandler block 的两个版本。如果 使用completionHandlerblock,代理回调将不会执行。

NSURLSessionTask工厂方法

NSURLSessionTask的工厂方法可以根据我们不同的需求返回不同的 task,这些task 不会立即运行,允许我们进一步的配置,然后可以使用 resume 方法来让它开始运行。

Datatask 可以通过 NSURLNSURLRequest 创建

NSURL *URL = [NSURL URLWithString:@"http://example.com"];
 NSURLRequest *request = [NSURLRequest requestWithURL:URL];

 NSURLSession *session = [NSURLSession sharedSession];
 NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                         completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
         // ...
     }];
 [task resume];

Uploadtask 的创建需要使用一个 request,另外加上一个要上传的 NSData 对象或者是一个本地文件的路径对应的 NSURL:

NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];
 NSURLRequest *request = [NSURLRequest requestWithURL:URL];
 NSData *data = ...;

 NSURLSession *session = [NSURLSession sharedSession];
 NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request
                                                            fromData:data
                                                   completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
         // ...
     }];

 [uploadTask resume];

Download task 也需要一个 request或者Url,不同之处在于 completionHandler 这个 block。Datataskuploadtask 会在任务完成时一次性返回,但是 Download task是将数据一点点地写入本地的临时文件。所以在 completionHandler 这个 block 里,我们需要把文件从一个临时地址移动到一个永久的地址保存起来:

NSURL *URL = [NSURL URLWithString:@"http://example.com/file.zip"];
 NSURLRequest *request = [NSURLRequest requestWithURL:URL];

 NSURLSession *session = [NSURLSession sharedSession];
 NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request
                                                         completionHandler: ^(NSURL *location, NSURLResponse *response, NSError *error) {
        NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
        NSURL *documentsDirectoryURL = [NSURL fileURLWithPath:documentsPath];
        NSURL *newFileLocation = [documentsDirectoryURL URLByAppendingPathComponent:[[response URL] lastPathComponent]];
        [[NSFileManager defaultManager] copyItemAtURL:location toURL:newFileLocation error:nil];
    }];

 [downloadTask resume];

上面介绍了几种 NSURLSessionTask 的基本使用,都是通过completionHandler这个 block 来进行任务处理回调的,下来我们看看他的代理都有哪些,以及使用

NSURLSession代理

在我们请求过程中,如果想要监听网络操作过程中发生的事件,比如我们下载一个大文件的时候,如果要等到下载完成可能会需要比较长的事件,这时候更好的体验是能够提供一个下载进度。类似这样的事件我们就需要用到代理。

我们在使用三种 task 的任意一种的时候都可以指定相应的代理。NSURLSession 的代理对象结构如下:


QQ140750@2x.png
  • NSURLSessionDelegate 作为所有代理的基类,定义了网络请求最基础的代理方法。
  • NSURLSessionTaskDelegate - 定义了网络请求任务相关的代理方法。
  • NSURLSessionDownloadDelegate - 用于下载任务相关的代理方法,比如下载进度等等。
  • NSURLSessionDataDelegate - 用于普通数据任务和上传任务。

下面以下载为例,我们看下代码如何实现的

- (void)downloadtask{
    
    //  服务器地址
    NSURL *url = [NSURL URLWithString:@"http://d1.music.126.net/dmusic/NeteaseMusic_1.5.10_632_web.dmg"];
    //  创建 SessionConfiguration配置
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    //  创建session实例并设置代理,用于监听下载
    NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
    //  创建 task
    self.task = [session downloadTaskWithURL:url];
    // 开始执行
    [self.task resume];
}

#pragma -mark NSURLSessionDownloadDelegate

/* 当下载任务完成下载时调用*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
                              didFinishDownloadingToURL:(NSURL *)location{
    
    // location是一个temp文件下的临时路径,自己需要保存
    NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil];
}

/* 定期发送通知委托下载进度. */
- (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
                                      didResumeAtOffset:(int64_t)fileOffset
                                     expectedTotalBytes:(int64_t)expectedTotalBytes{
}

/* 任务完成调用,查看是否成功 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{

    if (!error){
        NSLog(@"下载完成");
    }else{
        NSLog(@"下载失败");
    }
}

通过实现NSURLSessionDownloadDelegate 协议,就可以实现接收下载完成的通知,下载进度变化的通知,以及下载进度恢复的通知。

总结

NSURLSession 除了我们介绍的支持 task 特性,NSURLSessionConfiguration 配置对象,以及代理之外还提供了很多关于网络请求的相关特性,比如缓存控制,Cookie 控制,HTTP 验证操作等等。总之 NSURLSession 简单的接口之外,也提供了强大的体系。

NSURLSession 相比 AFN这些第三方库来说也有一些不足,比如它没有提供很方便的自动数据类型转换。比如,AFN 中可以自动将服务端返回的 JSON 数据识别并解析出来,而使用 NSURLSession 则需要自己来完成。

后续有时间我会继续分析一下 NSURLSession在 AFN 中的使用!

参考链接:
https://developer.apple.com/documentation/foundation/nsurlsession
https://swiftcafe.io/2015/12/20/nsurlsession
http://www.cocoachina.com/ios/20180108/21778.html

推荐阅读更多精彩内容