I have a a few threads that needs to run constantly or be locked at my disposal. When I lock the phone or swap applications the thread seems to halt until the application is back in focus.
I have a class Worker that is a sub class of NSThread. There is a method called start which is called by the firstViewController that creates the Worker object.
//method start
[self performSelectorInBackground:#selector(run) withObject:self];
What do I need to do to make my thread run all the time, rather than only running while in focus?
Thanks :)
When your app enters the background, all threads as suspended - unless you've configured your application to use multi-tasking, and your work is being done using the multi-tasking methods. This is detailed at Apple developer.
In short, you basically can't have a thread running constantly in the background on iOS if you want to be accepted in the App store, unless you're a navigation or VOIP application. You can have a thread continue to run for around 10 minutes after you enter the background, but that's it.
Im developing an app that has to run in the background. It's a location based app, so it runs all the time, the OS doesn't kill it.
It should send some info every 10 secs(just for debugging), I set a timer once its in the background. I set a breakpoint in the function that should be executed every 10 secs, which is never called, but if I pause the app and then continue the timer is called, and then the timer is executed every 10 secs without problems, weird right?
I thought that the timer would be executing anyway when I wasn't debugging, but it isn't, same thing as if I didn't pause the debugging.
My question is WHY?? The timer is set correctly(I assume) since it works after pausing, but it's not.
Any ideas?
The way I set the timer is:
self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(doStuff) userInfo:nil repeats:YES];
And in the function I connect to a webservice.
Thanks.
I have a similar app design and was stuck on the same thing. What I found somewhere on the internet is adding this type of statement applicationDidEnterBackground:
UIBackgroundTaskIdentifier locationUpdater =[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:locationUpdater];
locationUpdater=UIBackgroundTaskInvalid;
} ];
This tells the os that you still have things going and not to stop it.
I have my timer attached to this function
//this is a wrapper method to fit the required selector signature
- (void)timeIntervalEnded:(NSTimer*)timer {
[self writeToLog:[NSString stringWithFormat:#"Timer Ended On %#",[NSDate date]]];
[self startReadingLocation];
[timer invalidate];
timer=nil;
}
I set the timer in my my location manager delegate methods.
I feel your pain. I found that these things were super finicky. This is what worked for me. I hope it helps. I have found that there isn't any really restrictions in what you can do in the background.
There might be restrictions on what you can do in the background. Try adding the timer to the run loop before going into the background. Even that might not work; it may be that the only code of yours that can run in the background is the code called by the Core Location methods you've signed up for (e.g. locationManager:didUpdate...). But my impression is that timers already running before you start to go into the background will continue to run.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
for (int i =0; i<30; i++){
//add a local notification and schedule it
}
}
when app switch to background, these codes will freeze app in a while.
There's no document about UIApplication is thread safe or not.
After a long time test I found most time execute LocalNotification on background works well. but some times it just crash our app.
So it seems like all the class with 'UI' prefixed are not thread safe and you should never invoke there's methods on another thread.
And my solution is reduced the number of LocalNotification, it still freeze app in a bit, but we thing we can accept this little freeze.
By default, application processing freezes when the app goes to the background. The execution continues from the same statement where it left when the app comes back to the foreground. To execute code in the background, you have to surround it in the beginBackgroundTaskWithExpirationHandler block.
Have a look at the section Completing a Finite-Length Task in the Background at http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/BackgroundExecution/BackgroundExecution.html.
I'm using NSURLConnection to download resources asynchronously in iOS. (They are large-ish PDF files, so it takes some time on a slow connection.)
Now I'm updating my app from iOS 3 to iOS 4. As my app is none of location-aware, voip, and background music, I guess I need to do something.
My question is, then, what happens to the NSURLConnection currently running? Is it suspended and magically resumed when the app comes back to the foreground, or is it outright killed? If it is the latter, what is the standard strategy to resume it automatically later? Is there a open-source subclass of NSURLConnection which automatically does that?
You can start a task that will run for at most 10 minutes. Look at using the beginBackgroundTaskWithExpirationHandler: API for this purpose. Just be aware, if your task takes too long, it will be killed by the OS.
The NSURLConnection is indeed suspended and started again when the app enters the foreground. Just make sure you kill the connection if the app moves from suspended to not running like so:
- (void)applicationWillTerminate:(UIApplication *)application {
if (self.downloadConnection != nil){
[self.downloadConnection cancel];
}
}
In the Apple documentation for NSRunLoop there is sample code demonstrating suspending execution while waiting for a flag to be set by something else.
BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
I have been using this and it works but in investigating a performance issue I tracked it down to this piece of code. I use almost exactly the same piece of code (just the name of the flag is different :) and if I put a NSLog on the line after the flag is being set (in another method) and then a line after the while() there is a seemingly random wait between the two log statements of several seconds.
The delay does not seem to be different on slower or faster machines but does vary from run to run being at least a couple of seconds and up to 10 seconds.
I have worked around this issue with the following code but it does not seem right that the original code doesn't work.
NSDate *loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];
while (webViewIsLoading && [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate:loopUntil])
loopUntil = [NSDate dateWithTimeIntervalSinceNow:0.1];
using this code, the log statements when setting the flag and after the while loop are now consistently less than 0.1 seconds apart.
Anyone any ideas why the original code exhibits this behaviour?
Runloops can be a bit of a magic box where stuff just happens.
Basically you're telling the runloop to go process some events and then return. OR return if it doesn't process any events before the timeout is hit.
With 0.1 second timeout, you're htting the timeout more often than not. The runloop fires, doesn't process any events and returns in 0.1 of second. Occasionally it'll get a chance to process an event.
With your distantFuture timeout, the runloop will wait foreever until it processes an event. So when it returns to you, it has just processed an event of some kind.
A short timeout value will consume considerably more CPU than the infinite timeout but there are good reasons for using a short timeout, for example if you want to terminate the process/thread the runloop is running in. You'll probably want the runloop to notice that a flag has changed and that it needs to bail out ASAP.
You might want to play around with runloop observers so you can see exactly what the runloop is doing.
See this Apple doc for more information.
Okay, I explained you the problem, here's a possible solution:
#implementation MyWindowController
volatile BOOL pageStillLoading;
- (void) runInBackground:(id)arg
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Simmulate web page loading
sleep(5);
// This will not wake up the runloop on main thread!
pageStillLoading = NO;
// Wake up the main thread from the runloop
[self performSelectorOnMainThread:#selector(wakeUpMainThreadRunloop:) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void) wakeUpMainThreadRunloop:(id)arg
{
// This method is executed on main thread!
// It doesn't need to do anything actually, just having it run will
// make sure the main thread stops running the runloop
}
- (IBAction)start:(id)sender
{
pageStillLoading = YES;
[NSThread detachNewThreadSelector:#selector(runInBackground:) toTarget:self withObject:nil];
[progress setHidden:NO];
while (pageStillLoading) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
[progress setHidden:YES];
}
#end
start displays a progress indicator and captures the main thread in an internal runloop. It will stay there till the other thread announces that it is done. To wake up the main thread, it will make it process a function with no purpose other than waking the main thread up.
This is just one way how you can do it. A notification being posted and processed on main thread might be preferable (also other threads could register for it), but the solution above is the simplest I can think of. BTW it is not really thread-safe. To really be thread-safe, every access to the boolean needs to be locked by a NSLock object from either thread (using such a lock also makes "volatile" obsolete, as variables protected by a lock are implicit volatile according to POSIX standard; the C standard however doesn't know about locks, so here only volatile can guarantee this code to work; GCC doesn't need volatile to be set for a variable protected by locks).
In general, if you are processing events yourself in a loop, you're Doing It Wrong. It can cause a ton of messy problems, in my experience.
If you want to run modally -- for example, showing a progress panel -- run modally! Go ahead and use the NSApplication methods, run modally for the progress sheet, then stop the modal when the load is done. See the Apple documentation, for example http://developer.apple.com/documentation/Cocoa/Conceptual/WinPanel/Concepts/UsingModalWindows.html .
If you just want a view to be up for the duration of your load, but you don't want it to be modal (eg, you want other views to be able to respond to events), then you should do something much simpler. For instance, you could do this:
- (IBAction)start:(id)sender
{
pageStillLoading = YES;
[NSThread detachNewThreadSelector:#selector(runInBackground:) toTarget:self withObject:nil];
[progress setHidden:NO];
}
- (void)wakeUpMainThreadRunloop:(id)arg
{
[progress setHidden:YES];
}
And you're done. No need to keep control of the run loop!
-Wil
If you want to be able to set your flag variable and have the run loop immediately notice, just use -[NSRunLoop performSelector:target:argument:order:modes: to ask the run loop to invoke the method that sets the flag to false. This will cause your run loop to spin immediately, the method to be invoked, and then the flag will be checked.
At your code the current thread will check for the variable to have changed every 0.1 seconds. In the Apple code example, changing the variable will not have any effect. The runloop will run till it processes some event. If the value of webViewIsLoading has changed, no event is generated automatically, thus it will stay in the loop, why would it break out of it? It will stay there, till it gets some other event to process, then it will break out of it. This may happen in 1, 3, 5, 10 or even 20 seconds. And until that happens, it will not break out of the runloop and thus it won't notice that this variable has changed. IOW the Apple code you quoted is indeterministic. This example will only work if the value change of webViewIsLoading also creates an event that causes the runloop to wake up and this seems not to be the case (or at least not always).
I think you should re-think the problem. Since your variable is named webViewIsLoading, do you wait for a webpage to be loaded? Are you using Webkit for that? I doubt you need such a variable at all, nor any of the code you have posted. Instead you should code your app asynchronously. You should start the "web page load process" and then go back to the main loop and as soon as the page finished loading, you should asynchronously post a notification that is processed within the main thread and runs the code that should run as soon as loading has finished.
I’ve had similar issues while trying to manage NSRunLoops. The discussion for runMode:beforeDate: on the class references page says:
If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it returns after either the first input source is processed or limitDate is reached. Manually removing all known input sources and timers from the run loop is not a guarantee that the run loop will exit. Mac OS X may install and remove additional input sources as needed to process requests targeted at the receiver’s thread. Those sources could therefore prevent the run loop from exiting.
My best guess is that an input source is attached to your NSRunLoop, perhaps by OS X itself, and that runMode:beforeDate: is blocking until that input source either has some input processed, or is removed. In your case it was taking "couple of seconds and up to 10 seconds" for this to happen, at which point runMode:beforeDate: would return with a boolean, the while() would run again, it would detect that shouldKeepRunning has been set to NO, and the loop would terminate.
With your refinement the runMode:beforeDate: will return within 0.1 seconds, regardless of whether or not it has attached input sources or has processed any input. It's an educated guess (I'm not an expert on the run loop internals), but think your refinement is the right way to handle the situation.
Your second example just work around as you poll to check input of the run loop within time interval 0.1.
Occasionally I find a solution for your first example:
BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSRunLoopCommonModes beforeDate:[NSDate distantFuture]]);