NSURLSession的基础用法
- (void)viewDidLoad {
[super viewDidLoad];
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:kRemotePAJsonURL]];
req.HTTPMethod = @"POST";
NSURLSessionDataTask *dataTask = [urlSession dataTaskWithRequest:req];
[dataTask resume];
NSLog(@"本次dataTask:%@", dataTask);
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
NSLog(@"收到响应:%@ \ndataTask:%@", response, dataTask);
self.mData = [NSMutableData data];
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
completionHandler(disposition);
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
NSLog(@"%@收到data:%ld",[NSThread currentThread] ,data.length);
[self.mData appendData:data];
if (self.mData.length > 3000) {
[dataTask cancel];
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error
{
NSLog(@"完成, error:%@", error);
if (!error) {
//在完成的时候,之前收到的data怎么取到?不借助其他的变量,在该方法里取不到?
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:self.mData options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@", dict);
}
//不把本次session Invalidate,那么session持有的delegate不会被释放.
[session finishTasksAndInvalidate];
}
对于方法
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration
delegate:(nullable id <NSURLSessionDelegate>)delegate
delegateQueue:(nullable NSOperationQueue *)queue;
delegate和delegateQueue会被urlSession强引用.按苹果的文档说明:delegate会在URLSession:didBecomeInvalidWithError
结束后释放.但事实上,要想让delegate在didBecomeInvalidWithError
结束后释放,需要先把session Invalidate.否则,session持有的delegate不会被释放.这句话的正确理解应该是,当一个session invalidate后,delegate要等到URLSession:didBecomeInvalidWithError
结束后才会被释放.对于delegateQueue,实际使用时delegateQueue不能是主队列的.当delegateQueue不是主队列时,didReceiveData:方法将随机在某个线程执行.
基本上一个APP,生成一个urlSession就够了.没必要一次请求,创建一个session,请求结束后又将session Invalidate.因此也就没必要去管delegate和delegateQueue的内存释放问题,这三个对象基本上是等到APP结束才会销毁的.最佳做法之一就是使用GCD确保只生成一个session,然后使用该session来管理所有的请求.
对于代理方法: - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
在该方法中,为什么收到响应后,还要调用completionHandler block?
因为在该方法中,通过disposition参数,调用completionHandler后,可以更细粒度的控制本次请求是继续还是取消还是转为下载任务.如果是取消,则后面请求的响应体不会接收.如果是转为下载任务,那么通过调用completionHandler,NSURLSession将调用Delegate的 URLSession:dataTask:didBecomeDownloadTask:
方法并将新生成的Download task对象作为参数传入。在此调用之后,Delegate将不再接收来自Data task的回调消息,并开始接收Download task的回调消息。
注意:如果不调用
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
completionHandler(disposition);
后面的didReceiveData:代理方法将不会执行.
疑问
NSURLSession对象是被谁强引用了?如何释放?
NSURLSession对象应该是被系统的runloop强引用了,就类似于定时器一样,需要invalid后,才会被释放销毁.
题外话:如果timer属性是strong,那么invalidate后最好将其置为nil,否则invalid后timer因为还有人持有它,而不能销毁.strong情况下,timer的释放:[self.timer invalidate];self.timer = nil;
定时器对象是注册到runloop里的,应该通过invalidate来告诉runloop释放它.所以self不应该持有该对象,因此timer属性最好为weak.NSURLSession的生命周期?
在didCompleteWithError:完成的时候,之前收到的data怎么取到?不借助其他的变量,在该方法里取不到?