NSURLSession历史
在iOS7之后,苹果公司设计NSURLSession用来取代NSURLConnection.NSURLSession,NSURLSessionConfiguration,NSURLSessionTask.
NSURLSession 指的也不仅是同名类 NSURLSession,还包括一系列相互关联的类。NSURLSession 包括了与之前相同的组件,NSURLRequest 与 NSURLCache,但是把 NSURLConnection 替换成了 NSURLSession、NSURLSessionConfiguration 以及 NSURLSessionTask 的 3 个子类:NSURLSessionDataTask,NSURLSessionUploadTask,NSURLSessionDownloadTask。
与 NSURLConnection 相比,NSURLsession 最直接的改进就是可以配置每个 session 的缓存,协议,cookie,以及证书策略(credential policy),甚至跨程序共享这些信息。这将允许程序和网络基础框架之间相互独立,不会发生干扰。每个 NSURLSession 对象都由一个 NSURLSessionConfiguration 对象来进行初始化,后者指定了刚才提到的那些策略以及一些用来增强移动设备上性能的新选项。
NSURLSession 中另一大块就是 session task。它负责处理数据的加载以及文件和数据在客户端与服务端之间的上传和下载。NSURLSessionTask 与 NSURLConnection 最大的相似之处在于它也负责数据的加载,最大的不同之处在于所有的 task 共享其创造者 NSURLSession 这一公共委托者(common delegate)。
1. NSURLSession session类型
NSURLSession包括下面3种session类型
a). Default session(默认会话模式):
使用的是基于磁盘缓存的持久化策略,工作模式类似于原来的NSURLConnection,可以用来取代NSURLConnection中的:
[NSURLConnection sendAsynchronousRequest:queue:completionHandler:]
b). Ephemeral session(瞬时会话模式):
临时的进程内会话(内存),不会将cookie、证书、缓存储存到本地,只会放到内存中,当应用程序退出后数据也会消失。
c). Background session(后台会话模式):
和默认会话模式类似, 不过相比默认模式,该会话会在后台开启一个线程进行网络数据处理。
//Default session
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
//Ephemeral
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
//Background
+ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier
你需要了解包括NSURLSession、NSURLSessionConfiguration 以及 NSURLSessionTask 的 3 个子类:NSURLSessionDataTask,NSURLSessionUploadTask,NSURLSessionDownloadTask。
与 NSURLConnection 相比,NSURLsession 最直接的改进就是可以配置每个 session 的缓存,协议,cookie,以及证书策略,甚至跨程序共享这些信息。这将允许程序和网络基础框架之间相互独立,不会发生干扰。每个 NSURLSession 对象都由一个 NSURLSessionConfiguration 对象来进行初始化。
NSURLSession 中另一大块就是 session task。它负责处理数据的加载以及文件和数据在客户端与服务端之间的上传和下载。NSURLSessionTask 与 NSURLConnection 最大的相似之处在于它也负责数据的加载,最大的不同之处在于所有的 task 共享其创造者 NSURLSession 这一公共委托者(common delegate)。
2.NSURLSessionTask类
NSURLSessionTask是一个抽象子类,它有三个子类:NSURLSessionDataTask,NSURLSessionUploadTask和NSURLSessionDownloadTask。这三个类封装了现代应用程序的三个基本网络任务:获取数据,比如JSON或XML,以及上传和下载文件。
下面是其继承关系:
当一个 NSURLSessionDataTask 完成时,它会带有相关联的数据,而一个 NSURLSessionDownloadTask 任务结束时,它会带回已下载文件的一个临时的文件路径。因为一般来说,服务端对于一个上传任务的响应也会有相关数据返回,所以 NSURLSessionUploadTask 继承自 NSURLSessionDataTask。
所有的 task 都是可以取消,暂停或者恢复的。当一个 download task 取消时,可以通过选项来创建一个恢复数据(resume data),然后可以传递给下一次新创建的 download task,以便继续之前的下载。
不同于直接使用 alloc-init 初始化方法,task 是由一个 NSURLSession 创建的。每个 task 的构造方法都对应有或者没有 completionHandler 这个 block 的两个版本。
1).NSURLSessionDataTask
NSURLSessionDataTask使用NSData来交换数据. NSURLSessionDataTask不支持后台会话模式。
a) 通过request对象或url创建
//通过一个给定的请求
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
//通过一个给定的URL.
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;
代理方法:
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
completionHandler(NSURLSessionResponseAllow);
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
//data: response from the server.
}
b).通过request对象或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;
2).NSURLSessionDownloadTask
a).通过URL/request/resumeData创建
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;
//下载任务支持断点续传,这种方式是通过之前已经下载的数据来创建下载任务。
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
代理方法:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes;
b).通过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;
3).NSURLSessionUploadTask
a).通过request创建,在上传时指定文件源或数据源。
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;
代理方法:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
b).通过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;
3.NSURLSession类
从上面我们已经知道创建一个NSURLSessionTask需要NSURLSession,获取NSURLSession类对象有几种方式:
//使用全局的Cache,Cookie和证书。
+ (NSURLSession *)sharedSession;
//创建对应配置的会话。
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;
第三种方式指定了session的委托和委托所处的队列。当不再需要连接时,可以调用Session的invalidateAndCancel直接关闭,或者调用finishTasksAndInvalidate等待当前Task结束后关闭。这时Delegate会收到URLSession:didBecomeInvalidWithError:这个事件。Delegate收到这个事件之后会被解引用。
4.NSURLSessionConfiguration类
上面2、3种创建方式使用了NSURLSessionConfiguration,它用于配置会话的属性,创建方法如下:
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
//identifier参数指定了会话的ID,用于标记后台的session。
+ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;
这个类有两个属性:
@property BOOL allowsCellularAccess;
@property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(NA, 7_0);
allowsCellularAccess 属性指定是否允许使用蜂窝连接, discretionary属性为YES时表示当程序在后台运作时由系统自己选择最佳的网络连接配置,该属性可以节省通过蜂窝连接的带宽。在使用后台传输数据的时候,建议使用discretionary属性,而不是allowsCellularAccess属性,因为它会把WiFi和电源可用性考虑在内。
实例练习:
1.简单的get和post请求:
Get
- (void)get
{
// 1.创建一个NSURLSession
NSURLSession *session = [NSURLSession sharedSession];
// 2.利用NSURLSession创建一个任务(task)
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"]];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
NSLog(@"%ld, %@", res.statusCode, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
// 3.执行任务
[task resume];
}
Post
- (void)post
{
// 1.创建一个NSURLSession
NSURLSession *session = [NSURLSession sharedSession];
// 2.利用NSURLSession创建一个任务(task)
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/login"]];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
NSLog(@"%ld, %@", res.statusCode, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];
// 3.执行任务
[task resume];
}
2.使用NSURLSessionDataDelegate代理进行下载
#import "ViewController.h"
@interface ViewController ()<NSURLSessionDataDelegate>
@end
@implementation ViewController
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self download];
}
- (void)download
{
// 1.创建NSURLSession
/*
第一个参数: 全局的配置
第二个参数: 让谁成为session的代理
第三个参数: 告诉系统代理方法在哪个线程中执行
*/
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
// 2.创建Task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"];
NSURLSessionDataTask *task = [session dataTaskWithURL:url];
// 3.启动任务
[task resume];
}
#pragma mark - NSURLSessionDataDelegate
/*
接收到服务器的响应
session: 触发事件的对象
response: 服务器返回的请求头
completionHandler: 用于告诉系统, 我们是否需要接收数据
*/
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
NSLog(@"%s, %@", __func__, [NSThread currentThread]);
// 默认情况下NSURLSession的代理方法认为用户不需要接收数据, 所以不会调用其他接收数据的代理方法
// 如果需要接收数据, 那么必须在接收到服务器响应的时, 告诉系统我们需要接收数据
/*
NSURLSessionResponseCancel = 0, 取消任务, 默认实现方式
NSURLSessionResponseAllow = 1, 允许接收数据
NSURLSessionResponseBecomeDownload = 2, 将任务转换为下载任务
*/
completionHandler(NSURLSessionResponseAllow);
}
//接收到服务器的数据,该方法有可能打印一次或多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
NSLog(@"%s, %@", __func__, [NSThread currentThread]);
}
//请求完成,请求完成和请求错误都会调用这个方法,只不过如果是请求错误, 那么error有值, 如果没有错误error没有值
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"%s, %@", __func__, [NSThread currentThread]);
}
@end