NSURLSession与NSURLConnection区别

NSURLSession与NSURLConnection区别

2013 年的 WWDC 大会上,iOS 7.0 推出了 NSURLSession,对 Foundation URL 加载系统进行了彻底的重构,提供了更丰富的 API 来处理网络请求,如:支持 http2.0 协议、直接把数据下载到磁盘、同一 session 发送多个请求、下载是多线程异步处理和提供全局的 session 并可以统一配置等等,提高了 NSURLSession 的易用性、灵活性,更加地适合移动开发的需求。

在iOS9以后,NSURLConnection过期废弃.

NSURLSession 有三种网络操作方案:

  • 普通网络请求 NSURLSessionDataTask
  • 上传 NSURLSessionUploadTask
  • 下载 NSURLSessionDownloadTask

创建的 task 都是挂起状态,需要 resume 才能启动.

普通网络请求 和 上传网络请求

  • 普通: 当服务器返回的数据较小时,NSURLSession 与 NSURLConnection 执行普通任务的操作步骤没有区别。
  • 上传: 执行上传任务时,NSURLSession 与 NSURLConnection 一样需要设置 POST 请求的请求体进行上传。

下载任务方式

  • NSURLConnection下载文件时,先是将整个文件下载到内存,然后再写入到沙盒,如果文件比较大,就会出现内存暴涨的情况。
  • 而使用 NSURLSessionDownloadTask 下载文件,会默认下载到沙盒中的 tmp 文件中,不会出现内存暴涨的情况,但是在下载完成后会把 tmp 中的临时文件删除,需要在初始化任务方法时,在 completionHandler 回调中增加保存文件的代码。

请求方法的控制

  • NSURLConnection 实例化对象,实例化开始,默认请求就发送(同步发送),不需要调用 start 方法。而 cancel 可以停止请求的发送,停止后不能继续访问,需要创建新的请求。
  • NSURLSession 有三个控制方法,取消(cancel)、暂停(suspend)、继续(resume),暂停以后可以通过继续恢复当前的请求任务。

断点续传的方式

  • NSURLConnection 进行断点下载,通过设置访问请求的 HTTPHeaderField 的 Range 属性,开启运行循环,NSURLConnection 的代理方法作为运行循环的事件源,接收到下载数据时代理方法就会持续调用,并使用 NSOutputStream 管道流进行数据保存。

  • NSURLSession 进行断点下载,当暂停下载任务后,如果 downloadTask(下载任务)为非空,调用 cancelByProducingResumeData:(void (^)(NSData *resumeData))completionHandler 这个方法,这个方法接收一个参数,完成处理代码块,这个代码块有一个 NSData 参数 resumeData,如果 resumeData 非空,我们就保存这个对象到视图控制器的 resumeData 属性中,在点击再次下载时,通过调用[ [self.session downloadTaskWithResumeData:self.resumeData] resume]方法进行继续下载操作。

经过以上比较可以发现,使用 NSURLSession 进行断点下载更加便捷。

配置信息

NSURLSessionConfiguration 类的参数可以设置配置信息,其决定了 cookie安全高速缓存策略最大主机连接数资源管理网络超时等配置。NSURLConnection 不能进行这个配置,相比较与 NSURLConnection 依赖与一个全局的配置对象,缺乏灵活性而言,NSURLSession 有很大的改进了。

有三个方法来创建NSURLSessionConfiguration:

  • defaultSessionConfiguration 使用全局的cache,cookie,使用硬盘来缓存数据。

  • ephemeralSessionConfiguration 临时session配置,与默认配置相比,这个配置不会将缓存、cookie等存在本地,只会存在内存里,所以当程序退出时,所有的数据都会消失

  • backgroundSessionConfiguration 后台session配置,与默认配置类似,不同的是会在后台开启另一个线程来处理网络数据。

一旦创建了NSURLSessionConfiguration就可以给它设置各种属性

看NSURLSessionConfiguration的头文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
@interface NSURLSessionConfiguration : NSObject <NSCopying>

/* 三种创建方式 */

+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);

/* 当使用上述第三种方式创建后台sessionConfiguration时可以读到初始化时传入的唯一标识,其他创建方式都为空 */
@property (nullable, readonly, copy) NSString *identifier;

/*
缓存策略,默认值是NSURLRequestUseProtocolCachePolicy
*/
@property NSURLRequestCachePolicy requestCachePolicy;

/* 给request指定每次接收数据超时间隔,如果下一次接受新数据用时超过该值,则发送一个请求超时给该request。默认为60s */
@property NSTimeInterval timeoutIntervalForRequest;

/* 给指定resource设定一个超时时间,resource需要在时间到达之前完成。默认是7天。 */
@property NSTimeInterval timeoutIntervalForResource;

/* 指定网络传输类型。精切指出传输类型,可以让系统快速响应,提高传输质量,延长电池寿命等。
typedef NS_ENUM(NSUInteger, NSURLRequestNetworkServiceType)
{
NSURLNetworkServiceTypeDefault = 0, // 普通网络传输,默认使用这个
NSURLNetworkServiceTypeVoIP = 1, // 网络语音通信传输,只能在VoIP使用
NSURLNetworkServiceTypeVideo = 2, // 影像传输
NSURLNetworkServiceTypeBackground = 3, // 网络后台传输,优先级不高时可使用。对用户不需要的网络操作可使用
NSURLNetworkServiceTypeVoice = 4 // 语音传输
};
*/
@property NSURLRequestNetworkServiceType networkServiceType;

/* 是否使用蜂窝网络,默认是yes. */
@property BOOL allowsCellularAccess;

/* 是否由系统根据性能自动裁量后台任务。默认值是NO。同sessionSendsLaunchEvent一样,只对后台configuration有效。 */
@property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(10_10, 7_0);

/*
如果要为app的插件提供session,需要给这个值赋值
*/
@property (nullable, copy) NSString *sharedContainerIdentifier NS_AVAILABLE(10_10, 8_0);

/*
表示当后台传输结束时,是否启动app.这个属性只对 后台sessionConfiguration 生效,其他configuration类型会自动忽略该值。默认值是YES。
*/
@property BOOL sessionSendsLaunchEvents NS_AVAILABLE(NA, 7_0);

/*
指定了会话连接中的代理服务器。同样地,大多数面向消费者的应用程序都不需要代理,所以基本上不需要配置这个属性,默认为NULL
*/
@property (nullable, copy) NSDictionary *connectionProxyDictionary;

/* 确定是否支持SSLProtocol版本的会话
*/
@property SSLProtocol TLSMinimumSupportedProtocol;

/*
确定是否支持SSLProtocol版本的会话
*/
@property SSLProtocol TLSMaximumSupportedProtocol;

/*
它可以被用于开启HTTP管道,这可以显着降低请求的加载时间,但是由于没有被服务器广泛支持,默认是禁用的
*/
@property BOOL HTTPShouldUsePipelining;

/*
默认为yes,是否提供来自shareCookieStorge的cookie,如果想要自己提供cookie,可以使用HTTPAdditionalHeaders来提供。
*/
@property BOOL HTTPShouldSetCookies;

/* Policy for accepting cookies. This overrides the policy otherwise specified by the cookie storage. */
@property NSHTTPCookieAcceptPolicy HTTPCookieAcceptPolicy;

/*
指定了一组默认的可以设置出站请求的数据头。这对于跨会话共享信息,如内容类型,语言,用户代理,身份认证,是很有用的。
例如:
@{@"Accept": @"application/json",
@"Accept-Language": @"en",
@"Authorization": authString,
@"User-Agent": userAgentString
}
*/
@property (nullable, copy) NSDictionary *HTTPAdditionalHeaders;

/*
同时连接一个host的最大数。iOS默认是4.APP是作为一个整体来看的
*/
@property NSInteger HTTPMaximumConnectionsPerHost;

/*
存储cookie,清除存储,直接set为nil即可。
对于默认和后台的session,使用sharedHTTPCookieStorage。
对于短暂的session,cookie仅仅储存到内存,session失效时会自动清除。
*/
@property (nullable, retain) NSHTTPCookieStorage *HTTPCookieStorage;

/*
证书存储,如果不使用,可set为nil.
默认和后台session,默认使用的sharedCredentialStorage.
短暂的session使用一个私有存储在内存中。session失效会自动清除。
*/
@property (nullable, retain) NSURLCredentialStorage *URLCredentialStorage;

/*
缓存NSURLRequest的response。
默认的configuration,默认值的是sharedURLCache。
后台的configuration,默认值是nil
短暂的configuration,默认一个私有的cache于内存,session失效,cache自动清除。
*/
@property (nullable, retain) NSURLCache *URLCache;

/* Enable extended background idle mode for any tcp sockets created. Enabling this mode asks the system to keep the socket open
* and delay reclaiming it when the process moves to the background (see https://developer.apple.com/library/ios/technotes/tn2277/_index.html)
*/
@property BOOL shouldUseExtendedBackgroundIdleMode NS_AVAILABLE(10_11, 9_0);

/*
处理NSURLRequest的NSURLProtocol的子类。
重要:对后台Session失效。
*/
@property (nullable, copy) NSArray<Class> *protocolClasses;

@end

URLSessionTask

NSURLSessionTask是一个抽象类,其下有4个实体子类可以直接使用:NSURLSessionDataTask、NSURLSessionUploadTask、NSURLSessionDownloadTask、NSURLSessionStreamTask。这四个子类封装了现代程序四个最基本的网络任务:获取数据,比如JSON或者XML,上传文件和下载文件还有数据流的获取。

NSURLSession比NSURLConnection最方便的地方就是任务可以暂停,继续。在网络请求中,真正去执行下载或者上传任务的就是URLSessionTask,我们来看一下它常用的方法:
- (void)resume; 当使用NSURLSession创建一个NSURLSessionTask任务时,要手动调用此方法,任务才会开启,而NSURLConnection默认开启。
- (void)suspend; 暂停任务方法,手动调用会暂停当前任务,再次开启此任务时,会从紧接上次任务开始,会面会说到如何暂停任务再开启任务。
- (void)cancel; 取消任务。

NSURLSessionTask还有个属性,@property (readonly) NSURLSessionTaskState state; 此属性标识当前任务的状态,枚举类型

1
2
3
4
5
6
typedef NS_ENUM(NSInteger, NSURLSessionTaskState) {
NSURLSessionTaskStateRunning = 0, /* 正在执行 */
NSURLSessionTaskStateSuspended = 1, /* 暂停状态 */
NSURLSessionTaskStateCanceling = 2, /* 取消状态*/
NSURLSessionTaskStateCompleted = 3, /* 任务完成状态 */
}

上面说到的四个类,都直接或间接继承NSURLSessionTask,所有NSURLSessionTask的方法或者属性这四个类都有,那么,简单说一下这四个类都是干什么的。

NSURLSessionDataTask

NSURLSessionDataTask是开发中使用频率最高的,我们平常使用的GET和POST请求都是通过它来实现的,如果请求的数据简单并且不需要对获取的数据进行复杂操作,我们使用 Block 解析返回的数据即可。

另外我们也可以设置session的代理来实时的监听数据,我们可以使用NSURLSession的+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;这两个方法来设置代理,具体的协议为NSURLSessionDelegate,它有四个直接或间接子协议NSURLSessionTaskDelegateNSURLSessionDownloadDelegateNSURLSessionStreamDelegateNSURLSessionDataDelegate

NSURLSessionDownloadTask

NSURLSessionDownloadTask在下载文件的时候,是将数据一点点地写入本地的临时文件。所以在 completionHandler 这个 block 里,我们需要把文件从一个临时地址移动到一个永久的地址保存起来:

断点续传

说一下开发中经常用到的断点续传。在开发中,我们经常由于某种原因,在下载或上传的时候往往不能一次性下载或上传完,有可能下载或上传了一半就终止了,这时候当条件满足继续下载或上传时,我们不希望从头开始,这时候就可以使用断点续传。它的大概思路是:

  • 某种限制,续传暂停
  • 将暂停后数据(当前数据)保存起来–_resumeData = resumeData;
  • 条件允许续传时,使用resumeData创建新的NSURLSessionTask

NSURLSessionUploadTask

在 NSURLSession 中,文件上传主要使用两种方式:

1
2
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;

总结

  1. NSURLSession 是iOS7后出来的替代 NSURLConnection 的API;
  2. NSURLSession 通过绑定一个 delegate 在一个网络会话的生命周期里调用某些事件;
  3. NSURLSession 对象是线程安全的;
  4. NSURLSession 默认使用系统提供的delegate,并适当的使用现有的代码使用;
  5. NSURLSession 通过创建 NSURLSessionTask 代表资源被加载的任务,类似 NSURLConnection对象,但相比有更多可控制和定制的 delegate 模型;
  6. NSURLSessionTask 对象创建后是处于挂起状态,只有发-resume消息才会执行;
  7. NSURLSessionTask 的子类 在加载 Data 和 文件下载的使用上有所不同;
  8. NSURLSessionDataTask 接收到资源时会回调 RLSession:dataTask:didReceiveData: 代理method;
  9. NSURLSessionUploadTask 与 NSURLSessionDataTask 的构造方法不同,他需求显示的引用file或data object;
  10. NSURLSessionDownloadTask 会直接把response data写入到临时文件,网络会话结束后,delegate 会发送一个 URLSession:downloadTask:didFinishDownloadingToURL: 消息来处理下载文件,如果中途取消则会生成一个Blob Data以便下次恢复下载;
  11. 从iOS9开始,使用NSURLSessionStream可以通过一个给定的主机和端口直接建立TCP/IP连接;

NSURLSession的优点

  • 后台上传和下载: 只需在创建NSURLSession的时候配置一个选项,就能得到后台网络的所有好处;
  • 能够暂停和恢复网络操作: 使用NSURLSession API能够暂停,停止,恢复所有的网络任务,再也完全不需要子类化NSOperation;
  • 可配置的容器: 对于NSURLSession里面的requests来说,每个NSURLSession都是可配置的容器。举个例来说,假如你需要设置HTTP header选项,你只用做一次,session里面的每个request就会有同样的配置。
  • 提高认证处理:认证是在一个指定的连接基础上完成的。在使用NSURLConnection时,如果发出一个访问,会返回一个任意的request。此时,你就不能确切的知道哪个request收到了访问。而在NSURLSession中,就能用代理处理认证。
  • 丰富的代理模式: 在处理认证的时候,NSURLConnection有一些基于异步的block方法,但是它的代理方法就不能处理认证,不管请求是成功或是失败。在NSURLSession中,可以混合使用代理和block方法处理认证。

多看下 AFNetworking 的实现

希望对您有所帮助,您的支持将是我莫大的动力!