美文网首页iOS开发之常用技术点
[iOS] NSURLSession 介绍与使用

[iOS] NSURLSession 介绍与使用

作者: Leon_520 | 来源:发表于2018-11-01 14:22 被阅读37次

    简介

    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://objccn.io/issue-5-4/
    http://www.cocoachina.com/ios/20180108/21778.html
    https://swiftcafe.io/2015/12/20/nsurlsession

    相关文章

      网友评论

        本文标题:[iOS] NSURLSession 介绍与使用

        本文链接:https://www.haomeiwen.com/subject/ahrctqtx.html