NSTimer only fires sometimes? - iphone

Im running into an issue where my timer is randomly not working. I can literally STOP the app and rebuild it without changing any code. It works sometimes, and sometimes does not?
When I say "sometimes" I am talking about per app session. If it starts up, it will remain running. But it is not running every app session.
I am seeing the same thing when using both code blocks below.
//Code1:
//I tried this block of code after reading it might be related to how I am interacting with my UIElements while the timer is running in the background.
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
syncWithServerTimer = [NSTimer timerWithTimeInterval:15 target:self selector:#selector(syncWithServer) userInfo:nil repeats:YES];
[runloop addTimer:syncWithServerTimer forMode:NSRunLoopCommonModes];
[runloop addTimer:syncWithServerTimer forMode:UITrackingRunLoopMode];
//Code2:
syncWithServerTimer = [[NSTimer scheduledTimerWithTimeInterval:15 target:self selector:#selector(syncWithServer) userInfo:nil repeats:YES] retain];
Again, I tried both of these and they both work MOST of the time, but not ALL of the time.
What might be the issue? I am not prematurely releasing or invalidating.

So I guess its best to fire your timers on your Main Thread!
Seems to work everytime I do it now.
[self performSelectorOnMainThread:#selector(startTimer) withObject:nil waitUntilDone:NO];

the problem you are seeing (judging by your answer) is that the timer is destroyed with the run loop. if you schedule/add a timer on the run loop, the run loop will not run until all timers are exhausted.
if your timer should have the lifetime of the main thread or you cannot guarantee that the run loop will still be around, then add it to the main thread (although strictly, that is also not a guarantee that the timer will fire since the main run loop may exit).

Related

Whats the logic behind NSTimer when applicationDidEnterBackground [duplicate]

I don't understand it at all but NSTimer in my app definitely is running in background. I have a NSLog in method run by the timer and it is logging while it's in background. It's on iPhone 4 with iOS 4.2.1. I have declared location background support in Info.plist.
I read the docs and many discussions here and elsewhere and it shouldn't be possible. Is it an iOS bug? Or undocumented feature? I don't want to use it and find out in near future, for example with coming of iOS 4.3 that Apple silently "fixed" it and the app won't be working.
Does anybody know more about it?
NSTimer is going to fire whenever the main runloop is running. Apple makes no promises that I know of to unschedule timers or to prevent the main runloop from running. It's your responsibility to unschedule your timers and release resources when you move to the background. Apple isn't going to do it for you. They may, however, kill you for running when you are not supposed to or using too many seconds.
There are many holes in the system that will allow an app to run when it isn't authorized to. It would be very expensive for the OS to prevent this. But you cannot rely on it.
You can have a timer fire while in background execution mode. There are a couple of tricks:
You need to opt into background execution with beginBackgroundTaskWithExpirationHandler.
If you create the NSTimer on a background thread, you need to add it to the mainRunLoop manually.
- (void)viewDidLoad
{
// Avoid a retain cycle
__weak ViewController * weakSelf = self;
// Declare the start of a background task
// If you do not do this then the mainRunLoop will stop
// firing when the application enters the background
self.backgroundTaskIdentifier =
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:self.backgroundIdentifier];
}];
// Make sure you end the background task when you no longer need background execution:
// [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskIdentifier];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Since we are not on the main run loop this will NOT work:
[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(timerDidFire:)
userInfo:nil
repeats:YES];
// This is because the |scheduledTimerWithTimeInterval| uses
// [NSRunLoop currentRunLoop] which will return a new background run loop
// which will not be currently running.
// Instead do this:
NSTimer * timer =
[NSTimer timerWithTimeInterval:0.5
target:weakSelf
selector:#selector(timerDidFire:)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer
forMode:NSDefaultRunLoopMode];
// or use |NSRunLoopCommonModes| if you want the timer to fire while scrolling
});
}
- (void) timerDidFire:(NSTimer *)timer
{
// This method might be called when the application is in the background.
// Ensure you do not do anything that will trigger the GPU (e.g. animations)
// See: http://developer.apple.com/library/ios/DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW47
NSLog(#"Timer did fire");
}
Notes
Apps only get ~ 10 mins of background execution - after this the timer will stop firing.
As of iOS 7 when the device is locked it will suspend the foreground app almost instantly. The timer will not fire after an iOS 7 app is locked.

NSThread causes UIWebView Crash

i would like to explain my issue from the beginning. I am creating a app with UIWebView. Also I am capturing the screen at the moment user using the app and create a video using that image array.
I am using NSTimer to get screen shots of the screen. I worked well but i got one issue. That was when user start scrolling the web page or when user hold his touch on the screen NSTimer got pause and after he releases the touch NSTimer continue.
Here is my NSTimer used to get Screen shots of the screen.
assetWriterTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f/24.0f
target:self
selector:#selector (writeSample)
userInfo:nil
repeats:YES];
I googled this issue and most of the people got same issue as i got. So they found a answer for that and that is using NSRunLoop. They said it stops pausing the NSTimer.
So I used NSRunLoop and add NSTimer into the NSRunLoop and here is the code after I did that.
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
assetWriterTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f/24.0f
target:self
selector:#selector (writeSample)
userInfo:nil
repeats:YES];
[runLoop addTimer:assetWriterTimer forMode:NSRunLoopCommonModes];
[runLoop run];
This code worked well and it didnt stops pausing NSTimer. But after i used NSRunLoop My app got really really slow. Also it took lots of time to load the webpage into the WebView and scrolling also very slow.
So I started to search in google again and i found the issue. That was all those things are doing in main thread and iphone cant process every thing in same time. So people suggest to use NSThread to over come the problem. So what i did was create a NSThread and attach the process to the custom thread.
Here is my code after i apply NSThread to it.
[NSThread detachNewThreadSelector:#selector(launchTimer) toTarget:self withObject:nil];
-(void)launchTimer{
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
assetWriterTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f/24.0f
target:self
selector:#selector (writeSample)
userInfo:nil
repeats:YES];
[runLoop addTimer:assetWriterTimer forMode:NSRunLoopCommonModes];
[runLoop run];
}
-(void) writeSample{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//did some coding here to take screen shot
[pool release];
}
This solves the slowing the app issue forever and my app is running again very smoothly but it brings up serious problem now. That is happening because of using threads i guess. My custom thread and main thread getting conflict now and causes the app to crash. I searched the google for the reason. I found some questions related to this kind of problem and some people answered those and said Any UI changes need to be done in Main thread. Also UIWebView WebThread must run in main thread.
Those are some error reports i am getting,
Those errors are like some thread conflicting. But I have no idea how to solve this problem. I used my code to some native app that is not having any Web Views or Maps. So it works well with out crashing. But when i use this to app that is having WebView or Map its crashing some times and give me this errors.
I am in soo much trouble in here and If any one can give any idea or sample code to handle threads , handle thread priorities , or using NSTimer and NSRunLoop with out using NSThreads , or Overcome the app slowness without using NSThread , Any answer would be very very very helpful and I am looking forward to hear from you soon.
Thanks.
My guess is the code that you wrote to capture screen shots is causing the problem. A simple test would be to comment out all the code in the writeSample method and see if the app still crashes. I say this because you are right that all UI related tasks should work in the Main Thread at all times.
I would suggest to use a separate thread for timer so that your timer isn't paused and then from the writeSample method force the screen capture code to run on the main thread.
Hope this helps
First off:
As soon as you are using timers, you are working with run loops — as you can easily see from the reference:
Timers work in conjunction with run loops.
(That’s the first sentence of the second paragraph)
With that out of the way, to your syptoms:
Blocking a thread by capturing images, causes it to stutter.
This shouldn’t surprise you!
Moving the work off of that thread, releases the strain on the first one.
Shocking, I know!
Well, given that you didn’t show us any of the code that causes your synchronization issues, (read: crashes) this sarcastic answer is all the help I can offer you.
That and the tip to read and internalize the document I linked to above…
Edit
I just forgot to mention, this:
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
assetWriterTimer = [NSTimer scheduledTimerWithTimeInterval:blablabla];
[runLoop addTimer:assetWriterTimer forMode:NSRunLoopCommonModes];
[runLoop run];
doesn’t make much sense because…
-scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:
Creates and returns a new NSTimer object and schedules it on the current run loop in the default mode.
(Which is explained in the magnificent documentation, as well.)
In essence: when you’re on your own private thread, the default mode is sufficient. You have to make sure that the run loop is actually running, though.
This is actually accomplished by the last line of that code snippet, but it effectively causes your method to not return until the timer is cancelled…

updating screen every minute automatically on iPhone

I have a countdown TextField which I like to update every minute automatically in my iPhone app.
When the specific screen is visible it should count down - when switching to another screen the countdown obviously must not be updated.
When returning to the screen the countdown should continue again...
What would be the correct way of handling this?
Many thanks!
I would use an NSTimer. Here is the documentation for NSTimer.
Here is a snippet of code on how to set up a timer and get it to call a method:
NSTimer *theTimer = [NSTimer timerWithTimeInterval:60 target:self selector:#selector(updateTime) userInfo:nil repeats:NO];
This timer will call the updateTime method every time it fires. I have set it to NOT repeat, and instead, I would create a new timer each time I call the updateTime method. That way if you leave the UIViewController behind it will not keep firing the NSTimer.
Here is a generic updateTime method:
-(void) updateTime
{
//Update user interface
NSTimer *theTimer = [NSTimer timerWithTimeInterval:60 target:self selector:#selector(updateTime) userInfo:nil repeats:NO];
}
The timeInterval is in seconds, so this timer will keep firing every 60 seconds. You may wish to cut it a little shorter, like 30 seconds, or even 1 second and check the system time to see when you need to update your countdown.
I hope that helps!

NSTimer expires when the device is locked

My application supports the GPS and it runs both in foreground and background. I have started a timer , which is working perfectly unless the device is locked.
I want the timer to keep on running even when the device is locked.
Can anyone tell me how this can be done ?
Whichever way you start a timer, the default run mode is like below:
Timer *timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(run) userInfo:nil repeats:YES];
[[ NSRunLoop currentRunLoop ] addTimer:timer forMode:NSDefaultRunLoopMode ];
using NSDefaultRunLoopMode. You want more flexibility, so change forMode to take NSRunLoopCommonModes instead and you should be fine.

NSTimer doesn't work without UIApplication?

I have a custom iPhone application which doesn't rely on UIKit in any way (it is not linked to UIKit). This means that I'm not using UIApplication and therefore not calling UIApplicationMain.
The problem is that when I create a timer it never fires. The timer is created like that:
[NSTimer timerWithTimeInterval:10 target:self selector:#selector(updateTime) userInfo:nil repeats:TRUE];
I am suspecting that the problem might be run-loop related as after I finish basic initialization in "main()" , I do this:
NSLog(#"Starting application ...");
applicationInstance = [MyApp alloc];
[applicationInstance setMainLayer:mainLayer];
[NSThread detachNewThreadSelector:#selector(runApplication) toTarget:applicationInstance withObject:nil];
CFRunLoopRun();
I reverse-engineered UIKit and noticed that UIApplication does much more things like registering for system events etc. NSTimer might be relying on one of those things. The other thing I've noticed is that in UIApplication's delegate the thread is the same as in main().
How do I get NSTimer to work properly? If it is run-loop related, can someone point out the error? "CFRunLoopRun()" is used to hang up the main thread so the rest of the application can run. I don't think it is right though.
Edit: I think the run loop needs to be configred. Somehow ...
Your problem is definitely run loop-related; see the NSTimer reference, specifically, the introduction:
Timers work in conjunction with run loops. To use a timer effectively, you should be aware of how run loops operate—see NSRunLoop and Threading Programming Guide. Note in particular that run loops retain their timers, so you can release a timer after you have added it to a run loop.
The solution was pretty simple
NSTimer * timeUpdateTimer = [NSTimer timerWithTimeInterval:1 target:self selector:#selector(updateTime) userInfo:nil repeats:TRUE];
[[NSRunLoop mainRunLoop] addTimer:timeUpdateTimer forMode:NSDefaultRunLoopMode];
Thanks for the tip Shaggy Frog