相同点:
一、当服务器返回的数据较小时,NSURLSession与NSURLConnection执行普通任务的操作步骤没有区别;
二、 执行上传任务时,NSURLSession与NSURLConnection一样同样需要设置POST请求的请求体进行上传;
- 1、比如进行普通的Get/Post请求
说明:任何NSURLRequest默认都是get请求。
- (1)创建一个NSURL对象,设置请求路径(设置请求路径)
- (2)传入NSURL创建一个NSURLRequest对象,设置请求头和请求体(创建请求对象
- (3)使用NSURLConnection发送NSURLRequest(发送请求)
NSURLConnetion:
方法1:NSURLConnetion通过类方法直接发送request,通过闭包处理异步操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22- (void)connetionPost {
NSString *username = self.usernameText.text;
NSString *pwd = [self base64Encode:self.pwdText.text];
NSString *urlString = @"http://10.0.1.7/login.php";
urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
NSString *bodyString = [NSString stringWithFormat:@"username=%@&password=%@", username, pwd];
request.HTTPBody = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
// 同步请求,代码会阻塞在这里一直等待服务器返回,如果data为nil则请求失败,当获取少量数据时可以使用此方法。
// sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse * _Nullable * _Nullable)response error:(NSError **)error
// 注意,block的执行线程为queue,如果block涉及到UI操作,则必须回到主线程:[NSOperationQueue mainQueue]
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
// 数据处理代码...
}];
}
方法2:获取NSURLConnetion对立对象,将对象加入到runloop,通过delegate回调获取数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//创建连接有三个方法需要了解一下
// 该方法是NSURLConnection的默认构造函数,startImmediately参数,如果为YES,代表会将当前的connection实例加入到当前的runloop中,该connection的delegate的回调方法都会在当前线程执行,自动实现调度,所以这种情况下甚至是不需要调用 -start方法来开始请求;
//如果为NO,则需要手动调度,将当前的connection加入到一个线程的runloop中(如果不添加,默认会添加到当前线程的runloop中)。
- (nullable instancetype)initWithRequest:(NSURLRequest *)request delegate:(nullable id)delegate startImmediately:(BOOL)startImmediately startImmediately;
//实质为调用用的 [self initWithRequest:request delegate:delegate startImmediately:YES];
- (nullable instancetype)initWithRequest:(NSURLRequest *)request delegate:(nullable id)delegate ;
//类方法,不需要手动调用[connection start],加入到当前的Runloop
+ (NSURLConnection*)connectionWithRequest:(NSURLRequest *)request delegate:(id)delegate;
self.connection = [NSURLConnection connectionWithRequest:self.request delegate:self];
// 设置运行循环
[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
// 设置代理工作队列
[self.connection setDelegateQueue:[[NSOperationQueue alloc] init]];使用代理模式需要实现NSURLConnectionDataDelegate协议,此协议提供了几个代理方法,涵盖了请求交互的几个步骤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//当接收到服务器响应时调用,response为响应头部
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
//接收到服务返回的数据时调用,多次调用,直至接受到全部数据,每次接受一部分数据,放入到data中
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
//数据接收完成是调用
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
//监控上传数据的进度,再上传数据时调用
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten
totalBytesWritten:(NSInteger)totalBytesWritten
totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite;
//请求错误(失败)的时候调用
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
NSURLSession:
1 | - (void)SessionPost { |
不同点:
一、 普通任务和上传
NSURLSession针对下载/上传等复杂的网络操作提供了专门的解决方案,针对普通、上传和下载分别对应三种不同的网络请求任务:NSURLSessionDataTask
, NSURLSessionUploadTask
和 NSURLSessionDownloadTask
。创建的task都是挂起
状态,需要resume才能执行。
二、 下载任务方式
NSURLConnection下载文件时,先将整个文件下载到内存
,然后再写入沙盒,如果文件比较大,就会出现内存暴涨的情况。而使用NSURLSessionUploadTask下载文件,会默认下载到沙盒中的tem文件夹中,不会出现内存暴涨的情况,但在下载完成后会将tem中的临时文件删除
,需要在初始化任务方法时,在completionHandler回调中增加保存文件的代码。
具体差别还可以分为以下几个部分:
NSURLConnettion下载主要问题:
没有办法直接跟踪到下载的进度
会出现内存峰值
首先建立下载的请求:
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- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSString *urlString = @"http://dldir1.qq.com/invc/tt/QQBrowser_for_Mac.dmg";
urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
// NSURLRequestReloadIgnoringLocalCacheData 忽略本地的缓存数据
self.conn = [NSURLConnection connectionWithRequest:request delegate:self];
// 设置运行循环
[self.conn scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
// 设置代理工作队列,也就是代理回调会在子线程上进行
[self.conn setDelegateQueue:[[NSOperationQueue alloc] init]];
}
- (void)writeToSandbox:(NSData *)data {
NSFileHandle *fp = [NSFileHandle fileHandleForWritingAtPath:self.targetPath];
if (fp == nil) {
[data writeToFile:self.targetPath atomically:YES];
} else {
[fp seekToEndOfFile];
[fp writeData:data];
[fp closeFile];
}
}delegate回调处理数据:
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// 1. 接收到服务器响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// 保存要下载的文件的二进制数据长度,一边做进度观察
self.expectedContentLength = response.expectedContentLength;
// 清空当前下载的长度
self.currentLength = 0;
self.targetPath = [@"/Users/mac/Desktop" stringByAppendingPathComponent:response.suggestedFilename];
[[NSFileManager defaultManager] removeItemAtPath:self.targetPath error:NULL];
}
// 2. 接收到服务器数据
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// 1> 计算下载进度
self.currentLength += data.length;
float progress = (float) self.currentLength / self.expectedContentLength;
NSLog(@"%f %@", progress, [NSThread currentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
self.progressView.progress = progress;
});
// 2> 拼接文件数据
[self writeToSandbox:data];
}解决办法:
- 在
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
方法里面根据响应头返回缓存下来,然后在- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
每次接受的数据的时候用属性currentLength
保存已经下载的数据大小和currentLength
比较进行进度跟踪 - 如果采用
sendAsynchronousRequest:queue:completionHandler:
方法进行下载,只有下载完了completionHandler返回整个文件的NSData大小,会导致内存峰值飙升;解决此问题可以采用delegate的回调方法- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
每次接收到数据就写在硬盘上来避免
- 在
三、断点续传的方式
NSURLConnection
进行断点下载,通过设置访问请求的HTTPHeaderField
的Range
属性,开启运行循环,NSURLConnection
的代理方法作为运行循环的事件源,接收到下载数据时代理方法就会持续调用,并使 用NSOutputStream
管道流进行数据保存。1
2
3
4
5
6
7
8
9// 设置请求头信息,说明只需要请求该资源嗯一部分数据
/*
bytes=0-1000 表示下载0-1000的数据
bytes=0- 表示从0开始下载到下载完毕
bytes=100- 表示从100开始下载到下载完毕
*/
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSString *range = [NSString stringWithFormat:@"bytes=%zd-", self.currentSize];
[request setValue:range forHTTPHeaderField:@"Range"];NSURLSession
进行断点下载,当暂停下载任务后,如果downloadTask
(下载任务)为非空,调用- (void)cancelByProducingResumeData:(void (^)(NSData * _Nullable resumeData))completionHandler;
这个方法,这个方法接收一个参数,完成处理代码块,这个代码块有一个 NSData 参数resumeData
,如果resumeData
非空,我们就保存这个对象到视图控制器的resumeData
属性中。在点击再次下载时,通过调用[ [self.session downloadTaskWithResumeData:self.resumeData]resume]
方法进行继续下载操作。
经过以上比较可以发现,使用NSURLSession
进行断点下载更加便捷。
四、请求方法的控制
NSURLConnection
实例化对象,实例化开始,默认请求就发送(同步发送),不需要调用start
方法。而cancel
可以停止请求的发送,停止后不能继续访问,需要创建新的请求。NSURLSession
有三个控制方法,取消(cancel),暂停(suspend),继续(resume),暂停后可以通过继续恢复当前的请求任务
五、 配置信息
NSURLSession
的构造方法 sessionWithConfiguration:delegate:delegateQueue
中有一个 NSURLSessionConfiguration
类的参数可以设置配置信息,其决定了cookie
,安全和高速缓存策略,最大主机连接数,资源管理,网络超时等配置。NSURLConnection
不能进行这个配置,相比于 NSURLConnection
依赖于一个全局的配置对象,缺乏灵活性而言,NSURLSession
有很大的改进了。
NSURLSession可以设置三种配置信息,分别通过调用三个累方法返回配置对象:
1. `(NSURLSessionConfiguration *)defaultSessionConfiguration`,配置信息使用基于硬盘的持久话Cache,保存用户的证书到钥匙串,使用共享cookie存储;
2. `+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration` ,配置信息和default大致相同。除了,不会把cache,证书,或者任何和Session相关的数据存储到硬盘,而是存储在内存中,生命周期和Session一致。比如浏览器无痕浏览等功能就可以基于这个来做;
3. `+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier`,配置信息可以创建一个可以在后台甚至APP已经关闭的时候仍然在传输数据的session。注意,后台Session一定要在创建的时候赋予一个唯一的identifier,这样在APP下次运行的时候,能够根据identifier来进行相关的区分。如果用户关闭了APP,IOS 系统会关闭所有的background Session。而且,被用户强制关闭了以后,IOS系统不会主动唤醒APP,只有用户下次启动了APP,数据传输才会继续
六、其他
其中几点和NSURLConnetion相似:
Cookie 策略
HTTPCookieStorage存储了 session 所使用的 cookie。默认情况下会使用 NSHTTPCookieShorage的 +sharedHTTPCookieStorage这个单例对象,这与 NSURLConnection是相同的。
HTTPCookieAcceptPolicy决定了什么情况下 session 应该接受从服务器发出的 cookie。
HTTPShouldSetCookies指定了请求是否应该使用 session 存储的 cookie,即 HTTPCookieSorage属性的值。
安全策略
URLCredentialStorage存储了 session 所使用的证书。默认情况下会使用 NSURLCredentialStorage的 +sharedCredentialStorage这个单例对象,这与 NSURLConnection是相同的。TLSMaximumSupportedProtocol
和 TLSMinimumSupportedProtocol
确定 session 是否支持 SSL 协议。
缓存策略
URLCache是 session 使用的缓存。默认情况下会使用 NSURLCache的 +sharedURLCache这个单例对象,这与 NSURLConnection是相同的。requestCachePolicy指定了一个请求的缓存响应应该在什么时候返回。这相当于 NSURLRequest的 -cachePolicy方法。
具体可以参考从 NSURLConnection 到 NSURLSession。