Reputation: 11
I have some class A. In this class i have a method,
which calls [self performSelectorInBackground:...]
. And it starts downloading
some info from internet.
After i tap Home button, then enter the app again, this background method keeps working. So, if i call this method again, i have bad_access, because background method is already working and i call it twice.
Can i stop performing selector in background of the class A? For example in my applicationDidEnterBackground? Or can i check, if selector is performing or something?
I found couple things like
[[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget:a];
[NSObject cancelPreviousPerformRequestsWithTarget:a selector:@selector(startDownload) object:nil];
But they didn't work for me. So
my objAppDelegate:
@inteface ObjAppDelegate
{
A *a;
}
@implementation ObjAppDelegate
{
-(void)applicationDidEnterBackground:(UIApplication *)application
{
//or it can be didBecomeActive..
//here. check if background task of class A is running, or just stop it ??
}
}
@implementation A
{
//some timer, or event, etc.
-(void)startDownload
{
[self performSelectorInBackground:@selector(runBackgroundTask) withObject:nil];
}
-(void)runBackgroundTask
{
//some network stuff..
}
}
i did it like this:
threadForDownload = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain:) object:nil];
[threadForDownload start];
[self performSelector:@selector(startDownload) onThread:threadForDownload withObject:nil waitUntilDone:NO];
(void)threadMain:(id)data { NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSRunLoop *runloop = [NSRunLoop currentRunLoop]; [runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
while (YES) { [runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; }
[pool release]; }
In my startDownload method i look at activity indicator to check, whether startDownload is already running..
-(void)startDownload
{
if (![[UIApplication sharedApplication] isNetworkActivityIndicatorVisible]) // flag..
{
//....
}
}
// I make visible networkActivityIndicator every time i start downloading
Upvotes: 1
Views: 5723
Reputation: 86661
Here's what to do:
All of the operations on the property used to store the thread in have to be protected by @synchronized
or equivalent to prevent the main thread from sending cancel
to a deallocated thread object.
The background thread can't do IO operations that block for more than a short period of time. In particular, synchronous downloading of URLs using NSURLConnection is out. If you are using NSURLConnection, you'll want to move to the asynchronous methods and a run loop (arguably, in that case, you can do away with the background thread altogether). If you are using POSIX level IO, use poll()
with a timeout.
Upvotes: 4
Reputation: 16024
Run the method in a background thread, and keep a record of the NSThread
. Then later, you can just end the thread.
Upvotes: 1
Reputation: 293
I don't think that it would be save to force the interruption of a method. What you can do is to change the state of your object and check that state inside your method implementation to early return in case of a cancel (but don't forget to release allocated objects).
This is how NSOperationQueue works. From the documentation:
Cancelling an operation does not immediately force it to stop what it is doing. Although respecting the value returned by the isCancelled is expected of all operations, your code must explicitly check the value returned by this method and abort as needed.
Upvotes: 1
Reputation: 68016
You can easily create a BOOL
instance variable to determine whether background task is active.
BOOL isBackgroundTaskRunning;
Then in runBackgroundTask
if (isBackgroundTaskRunning) {
// already running
return;
}
isBackgroundTaskRunning = TRUE;
...
isBackgroundTaskRunning = FALSE;
Upvotes: 4