文章目录
NSURLSession,苹果对它的定位是作为NSURLConnection的替代者
NSURLSession的使用相对于之前的NSURLConnection更简单,而且不用处理Runloop相关的东西。
NSURLSession只提供了异步请求方式而没有提供同步请求方式
NSURLSession使用
- 创建一个USNSURLSession会话对象
- 根据会话创建task
- 执行task
NSURLSession的获取
- 获取共享的session
可以应对基本的网络请求,这样的session的可定制型很差但是可以满足基本的需求。
NSURLSession *session = [NSURLSession sharedSession];
- 获取自定义的session
在创建一个NSURLSession对象时需要传入一个NSURLConfiguration对象,这个NSURLConfiguration对象管理着网络连接的一些属性,比如是否允许网络使用蜂窝网络等,由同一个session创建的task共享一个NSURLConfiguration对象。传入的NSURLConfiguration对象的类型也决定了创建的NSURLSession的类型。
这里对于自定义的session的类型、NSURLConfiguration类型、以及几种初始化方式先不过多介绍
NSURLSessionTask
通过NSURLSession发起的每个请求,都会被封装为一个NSURLSessionTask任务,但一般不会直接是NSURLSessionTask类,而是基于不同任务类型,被封装为其对应的子类。
- NSURLSessionDataTask:处理普通的Get、Post请求。
- NSURLSessionUploadTask:处理上传请求,可以传入对应的上传文件或路径。
- NSURLSessionDownloadTask:处理下载地址,提供断点续传功能的cancel方法。
父类NSURLSessionTask中,部分常用方法
- resume
开始或继续请求,创建后的task默认是挂起的,需要手动调用resume才可以开始请求。 - suspend
挂起当前请求。主要是下载请求用的多一些,普通请求挂起后都会重新开始请求。下载请求挂起后,只要不超过NSURLRequest设置的timeout时间,调用resume就是继续请求。 - cancel
取消当前请求。任务会被标记为取消,并在未来某个时间调用URLSession:task:didCompleteWithError:方法。
创建Task
- NSURLSession提供有普通创建task的方式,创建后可以通过重写代理方法,获取对应的回调和参数。这种方式对于请求过程比较好控制。
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;
- NSURLSession也提供了block的方式创建task,创建方式简单如AFN,直接传入URL或NSURLRequest,即可直接在block中接收返回数据。和普通创建方式一样,block的创建方式创建后默认也是suspend的状态,需要调用resume开始任务。
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(nullable NSData *)bodyData completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;
completionHandler和delegate是互斥的,completionHandler的优先级大于delegate。相对于普通创建方法,block方式更偏向于面向结果的创建,可以直接在completionHandler中获取返回结果,但不能控制请求过程。
使用NSURLSession发送GET请求
这里仅举例NSURLSessionDataTask
- Error Domain=kCFErrorDomainCFNetwork Code=-1022 问题
解决:
info.plist文件配置
block方式创建task
- (void)test {
//1. 请求路径
NSURL *url = [NSURL URLWithString:@"http://news-at.zhihu.com/api/4/news/latest"];
//2. 获取会话
NSURLSession *session = [NSURLSession sharedSession];
//3. 创建请求对象
//请求对象内部默认已经包含了请求头和请求方法(GET)
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//4. 根据会话创建task
// 第一个参数:请求对象
// 第二个参数:completionHandler回调(请求完成【成功|失败】的回调)
// data:响应体信息(期望的数据)
// response:响应头信息,主要是对服务器端的描述
// error:错误信息,如果请求失败,则error有值
NSURLSessionDataTask *task2 = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
//解析服务器返回的数据
//此处返回的数据是JSON格式的,因此使用NSJSONSerialization进行反序列化处理
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSLog(@"%@", dic);
}
}];
// 发送get请求时,以上3、4步可换成下面注释掉的写法
//根据会话创建task
// 第一个参数:请求路径
// 第二个参数:completionHandler回调(请求完成【成功|失败】的回调)
// data:响应体信息(期望的数据)
// response:响应头信息,主要是对服务器端的描述
// error:错误信息,如果请求失败,则error有值
// 注意:
// 1)该方法内部会自动将请求路径包装成一个请求对象
// 该请求对象默认包含了请求头信息和请求方法(GET)
// 2)如果要发送的是POST请求,则不能使用该方法
// NSURLSessionDataTask *task1 = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// if (error == nil) {
// //解析服务器返回的数据
// //此处返回的数据是JSON格式的,因此使用NSJSONSerialization进行反序列化处理
// NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
// NSLog(@"%@", dic);
// }
// }];
//5. 启动任务
[task2 resume];
}
代理方法
//.m文件
#import "ViewController.h"
@interface ViewController () <NSURLSessionDataDelegate>
@property(nonatomic, strong) NSMutableData *data;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
_data = [[NSMutableData alloc] init];
[self test3];
}
- (void)test3 {
//请求路径
NSURL *url = [NSURL URLWithString:@"http://news-at.zhihu.com/api/4/news/latest"];
//创建请求对象
//请求对象内部默认已经包含了请求头和请求方法(GET)
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//获得会话对象,并设置代理
// 第一个参数:会话对象的配置信息defaultSessionConfiguration 表示默认配置
// 第二个参数:谁成为代理,此处为控制器本身即self
// 第三个参数:队列,该队列决定代理方法在哪个线程中调用
// 可以传主队列、非主队列
//NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//创建task
NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
[task resume];
}
//接收到服务器响应的时候调用该方法
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
NSLog(@"didReceiveResponse");
//注意:
//和NSURLConnection不同点在于接收到响应信息之后,
//需要使用completionHandler回调告诉系统应该如何处理服务器返回的数据
//默认是取消的(NSURLSessionResponseCancel),继续传递数据(NSURLSessionResponseAllow)
completionHandler(NSURLSessionResponseAllow);
}
//接收到服务器返回数据的时候会调用该方法,如果数据较大那么该方法可能会调用多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
//拼接服务器返回的数据数据
[_data appendData:data];
}
//当请求完成(成功\失败)的时候会调用该方法,如果请求失败,则error有值
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"didComplete");
if (!error) {
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:_data options:NSJSONReadingMutableContainers error:&error];
NSLog(@"%@", dic);
}
}
发送POST请求
- (void)test2 {
NSURL *url = [NSURL URLWithString:@"http://news-at.zhihu.com/api/4/news/latest"];
NSURLSession *session = [NSURLSession sharedSession];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//修改请求方式为POST
request.HTTPMethod = @"POST";
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error) {
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSLog(@"%@", dic);
}
}];
[task resume];
}
- NSURLSession的delegate为什么是强引用?
在初始化NSURLSession对象并设置代理后,代理对象将会被强引用。根据苹果官方的注释来看,这个强持有并不会一直存在,而是在调用URLSession:didBecomeInvalidWithError:方法后,会将delegate释放。
通过调用NSURLSession的invalidateAndCancel或finishTasksAndInvalidate方法,即可将强引用断开并执行didBecomeInvalidWithError:代理方法,执行完成后session就会无效不可以使用。也就是只有在session无效时,才可以解除强引用的关系。