NSURLSession 概述
想了解AFNetworking,需要先了解下NSURLSession。
NSURLSession 的优势
-
NSURLSession
支持http2.0
协议 -
在处理下载任务的时候可以直接把数据下载到磁盘
-
支持后台下载和上传
-
同一个
session
发送多个请求,只需要建立一次连接(复用TCP
) -
提供全局的
session
并且可以统一配置,使用更加方便 -
下载的时候是多线程异步处理,效率更高
NSURLSessionTask (抽象类)
使用NSURLSession
,共分两步:
-
通过
NSURLSession
的实例创建task
-
执行
task
这里的任务就是NSURLSessionTask
,NSURLSessionTask
是一个抽象类,如果要使用那么只能使用它的子类
NSURLSessionTask
有两个子类:
-
NSURLSessionDataTask
,可以用来处理一般的网络请求,如GET | POST
请求等;-
NSURLSessionDataTask
有一个子类为NSURLSessionUploadTask
,用于处理上传请求的时候有优势。
-
-
NSURLSessionDownloadTask
,主要用于处理下载请求,有很大的优势。
NSURLSession
的子类
NSURLSessionDataTask
这是一个和数据相关的任务,但其实dataTask
也可以胜任downloadTask
和uploadTask
的工作,这一般是我们使用最多的task
种类。
简单GET请求:
如果请求数据比较简单,也不需要对返回的数据做一些复杂的操作,我们可以使用带block
的api
。
//确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520&pwd=520&type=JSON"];
//创建 NSURLSession 对象
NSURLSession *session = [NSURLSession sharedSession];
/**
根据对象创建 Task 请求
url 方法内部会自动将 URL 包装成一个请求对象(默认是 GET 请求)
completionHandler 完成之后的回调(成功或失败)
param data 返回的数据(响应体)
param response 响应头
param error 错误信息
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:
^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//解析服务器返回的数据
NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
//默认在子线程中解析数据
NSLog(@"%@", [NSThread currentThread]);
}];
//发送请求(执行Task)
[dataTask resume];
注意:所有类型的
task
都要调用resume
方法才会开始进行请求。
简单POST请求
POST
和GET
的区别就在于request
,所以使用session
的POST
请求和GET
过程是一样的,区别就在于对request
的处理。
NSURL *url = [NSURL URLWithString:@"http://www.daka.com/login"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=daka&pwd=123" dataUsingEncoding:NSUTF8StringEncoding];
NSURLSession *session = [NSURLSession sharedSession];
// 由于要先对request先行处理,我们通过request初始化task
NSURLSessionTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]); }];
[task resume];
NSURLSessionDataDelegate代理方法
NSURLSession
提供了block
方式处理返回的数据的简便方式,但如果想要在接收数据过程中做进一步的处理,仍然可以调用相关的协议方法:NSURLSession
的代理方法和NSURLConnection
类似,都是分为接收响应、接收数据、请求完成几个阶段。
// 使用代理方法需要设置代理,但是session的delegate属性是只读的,要想设置代理只能通过这种方式创建session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
delegate:self
delegateQueue:[[NSOperationQueue alloc] init]];
// 创建任务(因为要使用代理方法,就不需要block方式的初始化了)
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.daka.com/login?userName=daka&pwd=123"]]];
// 启动任务
[task resume];
//对应的代理方法如下:
// 1.接收到服务器的响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
// 允许处理服务器的响应,才会继续接收服务器返回的数据
completionHandler(NSURLSessionResponseAllow);
}
// 2.接收到服务器的数据(可能调用多次)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
// 处理每次接收的数据
}
// 3.请求成功或者失败(如果失败,error有值)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
// 请求完成,成功或者失败的处理
}
注意点:
-
如果要使用代理方法,需要设置代理,但从
NSURLSession
的头文件发现session
的delegate
属性是只读的,因此设置代理要通过session
的初始化方法赋值:sessionWithConfiguration: delegate: delegateQueue:
其中:-
configuration
参数需要传递一个配置,我们暂且使用默认的配置[NSURLSessionConfiguration defaultSessionConfiguration]
就好。
-
delegateQueue
参数表示协议方法将会在哪个队列(NSOperationQueue
)里面执行。 -
-
NSURLSession
在接收到响应的时候要先对响应做允许处理:completionHandler(NSURLSessionResponseAllow);
,才会继续接收服务器返回的数据,进入后面的代理方法。值得一提的是,如果在接收响应的时候需要对返回的参数进行处理(如获取响应头信息等),那么这些处理应该放在前面允许操作的前面。
NSURLSessionDownloadTask
文件下载可以使用NSURLSessionDownloadTask
这个子类.
简单下载
NSURLSessionDownloadTask
同样提供了通过NSURL
和NSURLRequest
两种方式来初始化并通过block
来进行回调的方法。下面以NSURL
初始化为例:
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:@"http://www.daka.com/resources/image/icon.png"] ;
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
// location是沙盒中tmp文件夹下的一个临时url,文件下载后会存到这个位置,由于tmp中的文件随时可能被删除,所以我们需要自己需要把下载的文件挪到需要的地方
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
// 剪切文件
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:path] error:nil];
}];
// 启动任务
[task resume];
注意点:
-
需要将下载到
tmp
文件夹的文件转移到需要的目录。 -
response.suggestFilename
是从相应中取出文件在服务区上存储路径的最后部分,如数据在服务器的url
是http://www.daka.com/resources/image/icon.png
,那么气suggestFilename
就是icon.png
.
NSURLSessionDownloadDelegate代理方法
同样的,downloadTask
也提供了配套的代理方法
// 每次写入调用(会调用多次)
- (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
didFinishDownloadingToURL:(NSURL *)location {
// location还是一个临时路径,需要自己挪到需要的路径(caches下面)
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 task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
}
NSURLSessionUploadTask
在 NSURLSession
中,文件上传方式主要有以下两种:
NSURLSessionUploadTask *task = [[NSURLSession sharedSession] uploadTaskWithRequest:request
fromFile:fileName
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}];
和
NSURLSessionUploadTask *task = [[NSURLSession sharedSession] uploadTaskWithRequest:request
fromData:body
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"-------%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];
出于安全性考虑,通常我们会使用POST
方式进行文件上传,所以较多使用第二种方式。
但是NSURLSession
并没有为我们提供比NSURLConnection
更方便的上传方式,方法中body
处的参数需要填写request
请求体(http
协议规定格式的字符串)。
断点下载
NSURLSessionDownloadTask
提供了与断点下载相关的几个方法:
// 使用这种方式取消下载可以得到将来用来恢复的数据,保存起来
[self.task cancelByProducingResumeData:^(NSData *resumeData) {
self.resumeData = resumeData;
}];
// 由于下载失败导致的下载中断会进入此协议方法,也可以得到用来恢复的数据
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
// 保存恢复数据
self.resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData];
}
// 恢复下载时接过保存的恢复数据
self.task = [self.session downloadTaskWithResumeData:self.resumeData];
// 启动任务
[self.task resume];
其它
此外,task自身还拥有这几个方法:
- (void)suspend;
- (void)resume;
- (void)cancel;
suspend
可以让当前的任务暂停
resume
方法不仅可以启动任务,还可以唤醒suspend
状态的任务。
cancel
方法可以取消当前的任务,也可以向处于suspend
状态的任务发送cancel
消息,任务如果被取消便不能在恢复到之前的状态。
NSURLSessionConfiguration
NSURLSession
的配置信息,如:
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
// 超时时间
config.timeoutIntervalForRequest = 10;
// 是否允许使用蜂窝网络(后台传输不适用)
config.allowsCellularAccess = YES;
// 还有很多可以设置的属性
一般都是使用默认配置,但其实它的配置有三种类型:
// 将缓存存储在磁盘上
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
// 瞬时会话模式不会创建持久性存储缓存
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
// 后台会话模式,允许程序在后台进行上传下载工作
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier
注意点
1. 设置代理的强引用问题
-
NSURLSession
对象在使用的时候,如果设置了代理,那么session
会对代理对象保持一个强引用,在合适的时候应该主动释放。 -
可以在控制器调用
viewDidDisappear
方法的时候类进行处理,可以通过调用invalidateAndCancel
方法或者是finishTaskAndInvalidate
方法来释放对代理对象的强引用。 -
inivalidateAndCancel
方法直接取消请求然后释放代理对象-
finishTasksAdnInvalidate
方法等请求完成后释放代理对象。
[self.session finishTasksAndInvalidate];
-
2. 使用NSURLSession上传文件主要步骤和注意点
主要步骤:
-
确定上传请求的路径(
NSURL
) -
创建可变的请求对象(
NSMutableURLRequest
) -
修改请求方法为
POST
-
设置请求头信息(告知服务器端这是一个文件上传请求)
-
按照固定的格式拼接要上传的文件参数
-
根据请求对象穿件会话对象(
NSURLSession
对象) -
根据session对象来创建一个 uploadTask 上传请求任务
-
执行该上传请求任务(调用
resume
方法) -
得到服务器返回的数据,解析数据(上传成功/上传失败)
注意点
-
创建可变的请求对象,因为需要修改请求方法为
POST
,设置请求头信息 -
设置请求头这个步骤可能会被遗漏
-
要处理上传参数的时候,一定要按照固定的格式来进行拼接
-
需要采用合适的方法来获得上传文件的二进制数据类型(
MIMEType
,获取方式如下)-
对着该文件发送一个网络请求,接收到该请求响应的时候,可以通过响应头信息中的
MIMEType
属性得到 -
使用通用的二进制数据类型表示任意的二进制数据
application/octet-stream
-
调用
C 语言
的API
来获取
[self mimeTypeForFileAtPath:@"此处为上传文件的路径"]
。
-