美文网首页
iOS 网络(1)——NSURLSession

iOS 网络(1)——NSURLSession

作者: baochuquan | 来源:发表于2019-09-28 22:01 被阅读0次

    【原文链接】

    最近公司针对刚入职的应届毕业生开展了一个的“新牛计划”,目的是让他们能够在一个月的时间内从零基础成长为 iOS 开发新手。

    在这个过程中,我们需要承担讲师的角色。因此,我们对 iOS 开发的知识体系进行了划分,而我则负责讲解其中的 GCD 和网络相关部分。为此,我也算是学习了一下 iOS
    开发所涉及到的一些网络知识,也学习了一些开源框架,包括:AFNetworking、YTKNetwork、CocoaAsyncSocket。这里,我首先对 NSURLSession 做一些相关总结。后续,将陆续贴出相关开源框架的学习心得。

    NSURLSession 概述

    WWDC 2013,苹果对基于 NSURLConnection 的 Foundation URL 加载系统进行了重构,推出了新一代基于 NSURLSession 的 Foundation URL 加载系统,并将其首先应用在了 iOS 7 和 Mac OS X 10.9 Mavericks 系统之中。

    NSURLSession 架构

    image

    NSURLSession 这个名字,实际上是指代 Foundation 框架的 URL 加载系统中一些列相关的类和协议。上图所示为 NSURLSession 的系统架构图,主要由三个类构成:

    • NSURLSession
      • 负责请求/响应的关键对象,使用 NSURLSessionConfiguration 配置对象进行创建。
      • 在请求/响应的执行过程中调用 NSURLSessionTaskDelegate 所定义的各种代理方法。
    • NSURLSessionConfiguration
      • 用于对 NSURLSession 对象进行初始化,可以配置 可用网络Cookie安全性缓存策略自定义协议启动事件 等选项,以及用于移动设备优化的相关选项。
      • 几乎可以配置任何选项。
    • NSURLSessionTask
      • 一个抽象类,其子类可以创建不同类型的任务(Task),如:下载、上传、获取数据(如:JSON 或 XML)。
      • 在特定 URL Session 中执行。

    结合上述系统结构图,我们可以将 NSURLSession 中的类分为以下 6 种(如下图所示):

    • URL 加载(URL Loading)
    • 配置管理(Configuration Management)
    • 缓存管理(Cache Policy)
    • Cookie 存储(Cookie Storage)
    • 认证和证书(Authentication and Credentials)
    • 协议支持(Protocol Support)
    image

    在一个请求被发送到服务器之前,系统会先查询共享的缓存信息,然后根据 缓存策略(Cache Policy) 以及 可用性(availability) 的不同,一个已经被缓存的响应可能会被立即返回。如果没有缓存的响应可用,则这个请求将根据我们指定的策略来缓存它的响应,以便将来的请求可以使用。

    在一个请求被发送到服务器过程中,服务器可能会发出 鉴权查询(Authorization Challenge),这可以由共享的 Cookie 或 证书存储(Credential Storage) 来自动响应,或者由被委托对象来响应。此外,发送中的请求也可以被注册的 NSURLProtocol 对象所拦截,以便在必要时改变其加载行为。

    下面我们依次来详细介绍 URL 加载系统中的 3 个主要类: NSURLSessionTaskNSURLSessionNSURLSessonConfiguration。在 NSURLSessionConfiguration 中,我们将对缓存策略、Cookie 存储、自定义协议等内容稍作介绍。

    NSURLSessionTask

    NSURLSessionTask 是一个抽象类,其包含如下 3 个实体子类。这 3 个子类封装了 3 个最基本的网络任务:获取数据(如:JSON 或 XML)、上传文件下载文件

    • NSURLSessionDataTask
    • NSURLSessionUploadTask
    • NSURLSessionDownloadTask
    image

    上图所示为这些类之间的继承关系。对于 NSURLSessionDataTask,服务器会有响应数据;而对于上传请求,服务器也会有响应数据,所以 NSURLSessionUploadTask 继承自 NSURLSessionDataTaskNSURLSessionDownloadTask 完成时,会带回已下载文件的一个临时的文件路径。

    关于 NSURLSessionTask 的数据返回方式,主要有两种方式:

    • completionHandler 回调
    • NSURLSessionDelegate 代理

    通过 completionHandler 回调将会创建一个隐式的代理(delegate),从而替代该 Task 原来的代理 —— Session。

    对于需要 override 原有 Session Task 的代理的默认行为的情况,我们需要使用不带 completionHandler 版本。

    需要注意的是,NSURLSessionTask 及其子类都有着各自的代理协议,它们之间也存在着如下图所示的继承关系。

    image
    • NSURLSessionDelegate:定义了网络请求最基础的代理方法。作为所有代理的基类。
    • NSURLSessionTaskDelegate:定义了网络请求任务相关的代理方法。
    • NSURLSessionDownloadDelegate:定义了下载任务相关的代理方法,如:下载进度等
    • NSURLSessionDataDelegate:定义了普通数据任务和上传任务相关的代理方法。

    下面简要介绍一下这三个子类。

    NSURLSessionDataTask

    NSURLSessionDataTask 主要用于 读取服务端的简单数据,如:JSON、XML 数据。

    创建方法(基于 NSURLSession 对象)

    // 使用 NSURLRequest 对象创建
    - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
    
    // 使用 NSURL 对象创建
    - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;
    

    CompletionHandler

    - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;    
    
    - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
    

    NSURLSessionUploadTask

    NSURLSessionUploadTask 主要用于 向服务器发送文件类型的数据

    创建方法(基于 NSURLSession 对象)

    // 使用 NSURLRequest 对象创建,上传时指定文件源
    - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;  
    
    // 使用 NSURLRequest 对象创建,上传时指定数据源   
    - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;  
      
    - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;
    

    CompletionHandler

    - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;    
    
    - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
    

    NSURLSessionDownloadTask

    NSURLSessionDownloadTask 主要用于 文件下载,它针对大文件的网络请求做了更多的处理,如:下载进度、断点续传等。

    创建方法(基于 NSURLSession 对象)

    // 使用 NSURLRequest 对象创建
    - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;    
        
    // 使用 NSURL 对象创建
    - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;    
      
    // 使用之前已经下载的数据来创建
    - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
    

    CompletionHandler

    - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;    
    
    - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;    
    
    - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;
    

    NSURLSession

    NSURLSession 是负责请求/响应的关键对象,使用 NSURLSessionConfiguration 配置对象进行创建。

    NSURLSession 本身并不会进行请求,而是通过创建 Task 的形式来进行网络请求。同一个 NSURLSession 可以创建多个 Task,并且这些 Task 之间的 Cache 和 Cookie 是共享的。

    NSURLSession 在管理请求/响应的过程中会调用相关的代理方法。这些代理方法主要分两类:

    • Session 的委托对象实现的代理方法(NSURLSessionDelegate 定义的方法)
      • 主要用于处理连接层问题,如:服务器信任、客户端证书认证、NTLM 和 Kerberos 协议等问题
    • Task 的委托对象实现的代理方法(NSURLSessionTaskDelegate 及其子协议定义的方法)
      • 主要用于处理以网络请求为基础的问题,如:Basic,Digest,代理身份验证(Proxy Authentication) 等。

    NSURLSessionConfiguration

    NSURLSessionConfiguration 对象用于对 NSURLSession 进行初始化。

    NSURLSessionConfiguration 对以前 NSMutableURLRequest 所提供的网络请求层的设置选项进行了扩充,提供给开发者相当大的灵活性和控制权。从指定可用网络,到 cookie,安全性,缓存策略,再到使用自定义协议,启动事件的设置,以及用于移动设备优化的几个新属性,可以发现使用 NSURLSessionConfiguration 可以找到几乎任何想要进行配置的选项。

    NSURLSession 在初始化时会把配置它的 NSURLSessionConfiguration 对象进行一次深拷贝,并保存到自己的 configuration 属性中,而且这个属性是只读的。也就是说,configuration 只在初始化时被读取一次,之后都是不会变化的。

    初始化

    NSURLSessionConfiguration 有三个类工厂方法:

    • + defaultSessionConfiguration
      • 返回一个标准的配置,具有共享 NSHTTPCookieStorage、共享 NSURLCache、共享 NSURLCredentialStorage
    • + ephemeralSessionConfiguration
      • 返回一个预设的配置,该配置中不会对缓存、Cookie和证书进行持久性存储。这对于实现类似秘密浏览这种功能来说是很理想的。
    • + backgroundSessionConfiguration:(NSString *)identifier
      • 创建一个后台 Session。后台 Session 不同于普通 Session,后台 Session 可以在应用程序挂起、退出或崩溃的情况下进行上传/下载任务。初始化时指定的标识符,可用于向任何可能在进程外恢复后台传输的 守护进程(daemon) 提供上下文。

    属性配置

    NSURLSessionConfiguration 拥有数十个配置属性。熟练掌握这些配置属性的用处,可以让应用程序充分地利用其网络环境。

    常规配置

    @property(copy) NSDictionary *HTTPAdditionalHeaders
    

    HTTPAdditionalHeaders 为基于 configuration 的 Session 生成的所有 Task 中的 NSRULRequest 对象添加额外的请求头部字段。默认为空。

    NSURLSession 默认为 NSURLRequest 对象添加了如下请求头部字段:

    • Authorization
    • Connection
    • Host
    • Proxy-Authenticate
    • Proxy-Authorization
    • WWW-Authenticate

    如果在 HTTPAdditionalHeaders 自定义的头部字段与 NSURLRequest 对象重复了,则优先使用 NSURLRequest 对象中的请求头部字段。

    利用 HTTPAddtionalHeaders 可以添加如下这些请求头部字段:

    • Accept
    • Accept-Language
    • User-Agent
    • ...
    @property NSURLRequestNetworkServiceType networkServiceType
    

    指定网络传输类型。可以让操作系统快速响应,提高传输质量,延长电池寿命等。大多数应用程序都不需要设置。

    @property BOOL allowsCellularAccess
    

    是否使用蜂窝网络。默认是 YES

    @property NSTimeInterval timeoutIntervalForRequest
    

    指定请求的超时间隔。默认为 60s。

    @property NSTimeInterval timeoutIntervalForResource
    

    指定资源的超时间隔。默认是7天。

    Cookie 策略

    @property(retain) NSHTTPCookieStorage *HTTPCookieStorage;
    

    存储了 Session 所使用的 Cookie。默认情况下会使用 NSHTTPCookieStorage+ sharedHTTPCookieStorage 单例。

    @property BOOL HTTPShouldSetCookies;
    

    指定了请求是否应该使用 Session 存储的 Cookie,即 HTTPCookieStorage 属性的值。

    @property NSHTTPCookieAcceptPolicy HTTPCookieAcceptPolicy;
    

    决定了什么情况下 Session 应该接受从服务器发出的 Cookie。

    安全策略

    @property(retain) NSURLCredentialStorage *URLCredentialStorage;
    

    存储了 Session 所使用的证书。默认情况下会使用 NSURLCredentialStorage+ sharedCredentialStorage 单例。

    @property SSLProtocol TLSMaximumSupportedProtocol;
    @property SSLProtocol TLSMinimumSupportedProtocol;
    

    两者确定 Session 是否支持 SSL 协议。

    缓存策略

    @property(retain) NSURLCache *URLCache;
    

    Session 使用的缓存。默认情况下会使用 NSURLCache+ sharedURLCache 单例。

    @property NSURLRequestCachePolicy requestCachePolicy;
    

    指定了一个请求的缓存响应应该在什么时候返回。

    后台传输

    @property(readonly, copy) NSString *identifier
    

    仅当使用 backgroundSessionConfigurationWithIdentifier: 方法创建配置对象时,才会设置此属性的值。identifier 唯一标识 后台会话 对象。

    如果应用程序在后台任务进行传输时终止,可以使用 identifier 在应用程序重新启动时,重新创建 configurationsession 对象与 之前传输进行关联。

    @property BOOL sessionSendsLaunchEvents;
    

    设置传输结束时是否应该在后台恢复或启动应用程序。

    @property(getter=isDiscretionary) BOOL discretionary;
    

    设置后台 Task 是否可以由系统进行调度,从而获得最佳性能。

    @property BOOL shouldUseExtendedBackgroundIdleMode;
    

    设置应用程序切换至后台时是否保持打开 TCP 连接。

    自定义协议

    @property(copy) NSArray<Class> *protocolClasses;
    

    用来配置特定某个 Session 所使用的自定义协议(该协议是 NSURLProtocol 的子类)的数组。

    多路径 TCP

    @property NSURLSessionMultipathServiceType multipathServiceType;
    

    指定通过 Wi-Fi 和 蜂窝网络传输数据的多路径 TCP 的连接策略。

    HTTP 策略与代理

    @property NSInteger HTTPMaximumConnectionsPerHost;
    

    用于限制连接到特定主机的数量。

    @property BOOL HTTPShouldUsePipelining;
    

    用于开启 HTTP 流水线(HTTP pipelining),可以显着减少请求的加载时间,但是由于没有被服务器广泛支持,默认是 NO 的。

    @property(copy) NSDictionary *connectionProxyDictionary;
    

    指定了 Session 连接中的代理服务器

    NSURLSession 使用

    NSURLSession 的使用有如下几个步骤:

    1. 创建会话:基于 NSURLSessionConfiguration 对象创建 NSURLSession 对象
    2. 创建任务:基于 NSURLSession 对象创建 NSURLSessionTask 对象
    3. 执行任务:执行 NSURLSessionTask 对象

    创建会话

    会话的创建方式有三种:

    // 1. 直接创建,使用默认的 NSURLSessionConfiguration 配置
    NSURLSession *session = [NSURLSession sharedSession];
    
    // 2. 配置后创建,先初始化一个 NSURLSessionConfiguration 对象
    [NSURLSession sessionWithConfiguration:defaultSessionConfiguration];
    
    // 3. 设置加代理获得
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                                          delegate:self
                                                     delegateQueue:[[NSOperationQueue alloc] init]];
    

    创建任务

    任务的创建在上文介绍 NSURLSessionTask 时已经提到。这里不做赘述。

    执行任务

    // 执行任务
    [task resume];
    

    使用示例

    GET 请求

    // 1. 创建会话
    NSURLSession *session = [NSURLSession sharedSession];
    
    // 2. 创建任务
    NSURL *url = [NSURL URLWithString:@"http://www.xxx.com/login?username=myName&pwd=myPsd"];
    
    NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        NSLog(@"%@", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
        // 打印解析后的json数据
        // NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
    
    }];
    
    // 3. 执行任务
    [task resume];
    

    POST 请求

    // 1. 创建会话
    NSURLSession *session = [NSURLSession sharedSession];
    
    // 2. 创建任务
    NSURL *url = [NSURL URLWithString:@"http://www.xxx.com/login"];
    
    // 创建请求对象里面包含请求体
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    request.HTTPBody = [@"username=myName&pwd=myPsd" dataUsingEncoding:NSUTF8StringEncoding];
    
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
          
        NSLog(@"%@", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
        // 打印解析后的json数据
        // NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
    
    }];
    
    // 3. 执行任务
     [task resume];
    

    结论

    iOS 7 和 Mac OS X 10.9 Mavericks 中 URL 加载系统的变化,是对 NSURLConnection 进行深思熟虑后的一个自然而然的进化。尽管在这个体系结构中,某些决定对于可组合性和可扩展性而言是一种倒退,但是 NSURLSession 仍然是实现更高级别网络功能的一个强大的基础框架。

    参考

    1. URL Loading System
    2. URL Loading System 概览
    3. iOS NSURLSession 详解
    4. URLSession Tutorial: Getting Started
    5. NSMutableURLRequest
    6. 从 NSURLConnection 到 NSURLSession
    7. From NSURLConnection to NSURLSession

    (完)

    相关文章

      网友评论

          本文标题:iOS 网络(1)——NSURLSession

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