I have a strange problem when I try to invalidate an NSURLSession
instance.
The code is quite simple: I have a View Controller, two buttons (start: and stop:), and a text field for the url.
A simple extract of the code:
- (IBAction)start:(id)sender {
NSURLSessionConfiguration *conf = [NSURLSessionConfiguration backgroundSessionConfiguration:@"conf"];
self.session = [NSURLSession sessionWithConfiguration:conf delegate:self delegateQueue:nil];
NSURLSessionDownloadTask *task = [self.session downloadTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.url.text]]];
[task resume];
}
- (IBAction)cancel:(id)sender {
[self.session invalidateAndCancel];
}
or, if you prefer, the whole project: Link
Now, try to download a file ( http://download.thinkbroadband.com/1GB.zip ).
Since I want this download to continue in the background, I'm using a background session.
The session starts correctly and the download continue in the background, but if I try to cancel it (sending invalidateAndCancel
) I have a bad access.
Profiling with Zombie enabled give this zombie object: _NSCFBackgroundDownloadTask
.
So, if I retain the NSURLSessionDownloadTask
(using a strong property to store it) the bad access doesn't happen.
But, AFAIK, NSURLSession should retain it's tasks itself, so I would like to understand what's wrong with my code (maybe I'm missing something in the docs?) or if I should file a bugreport.
Thanks
Use a better background session configuration identifier, please! This is not really your session; a background session is a kind of gateway into a shared system session. You need to distinguish your session's tasks from those of all the other apps doing background uploading and downloading. Use something unique, like @"com.company.appname.specialname"
.
Canceling and invalidating a background session doesn't make much sense. You are killing the session; you'll never be able to use it again after invalidating it. That's silly. What you want to do is create a background session once, as your app launches, and just leave it there forever (as a gateway to the shared system session, as I said before). What you want to cancel, if you want to cancel something, is the task. Keep a reference to the task so you can say cancel
to that reference, if you think you're going to want to cancel it. Or, if you really don't want to keep a reference to the task, you can ask the NSURLSession for a list of your current tasks by calling getTasksWithCompletionHandler:
; a task can have an identifier, so there should be no problem finding the one you want and telling it to cancel
.