最近公司针对刚入职的应届毕业生开展了一个的“新牛计划”,目的是让他们能够在一个月的时间内从零基础成长为 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 架构
imageNSURLSession
这个名字,实际上是指代 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)
在一个请求被发送到服务器之前,系统会先查询共享的缓存信息,然后根据 缓存策略(Cache Policy) 以及 可用性(availability) 的不同,一个已经被缓存的响应可能会被立即返回。如果没有缓存的响应可用,则这个请求将根据我们指定的策略来缓存它的响应,以便将来的请求可以使用。
在一个请求被发送到服务器过程中,服务器可能会发出 鉴权查询(Authorization Challenge),这可以由共享的 Cookie 或 证书存储(Credential Storage) 来自动响应,或者由被委托对象来响应。此外,发送中的请求也可以被注册的 NSURLProtocol
对象所拦截,以便在必要时改变其加载行为。
下面我们依次来详细介绍 URL 加载系统中的 3 个主要类: NSURLSessionTask
、NSURLSession
、NSURLSessonConfiguration
。在 NSURLSessionConfiguration
中,我们将对缓存策略、Cookie 存储、自定义协议等内容稍作介绍。
NSURLSessionTask
NSURLSessionTask
是一个抽象类,其包含如下 3 个实体子类。这 3 个子类封装了 3 个最基本的网络任务:获取数据(如:JSON 或 XML)、上传文件、下载文件。
NSURLSessionDataTask
NSURLSessionUploadTask
NSURLSessionDownloadTask
上图所示为这些类之间的继承关系。对于 NSURLSessionDataTask
,服务器会有响应数据;而对于上传请求,服务器也会有响应数据,所以 NSURLSessionUploadTask
继承自 NSURLSessionDataTask
。NSURLSessionDownloadTask
完成时,会带回已下载文件的一个临时的文件路径。
关于 NSURLSessionTask
的数据返回方式,主要有两种方式:
completionHandler
回调NSURLSessionDelegate
代理
通过 completionHandler
回调将会创建一个隐式的代理(delegate),从而替代该 Task 原来的代理 —— Session。
对于需要 override 原有 Session Task 的代理的默认行为的情况,我们需要使用不带 completionHandler
版本。
需要注意的是,NSURLSessionTask
及其子类都有着各自的代理协议,它们之间也存在着如下图所示的继承关系。
-
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
在应用程序重新启动时,重新创建 configuration
和 session
对象与 之前传输进行关联。
@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
的使用有如下几个步骤:
- 创建会话:基于
NSURLSessionConfiguration
对象创建NSURLSession
对象 - 创建任务:基于
NSURLSession
对象创建NSURLSessionTask
对象 - 执行任务:执行
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
仍然是实现更高级别网络功能的一个强大的基础框架。
参考
- URL Loading System
- URL Loading System 概览
- iOS NSURLSession 详解
- URLSession Tutorial: Getting Started
- NSMutableURLRequest
- 从 NSURLConnection 到 NSURLSession
- From NSURLConnection to NSURLSession
(完)
网友评论