scheduledTimerWithTimeInterval is not calling at correct intervals sometimes - ios

I have two separate timers on for recording data at 0.25 seconds and other for recording location details at 1 sec as below
self.hardBrakingTimer = [NSTimer scheduledTimerWithTimeInterval:0.25
target:self
selector:#selector(timerFired:)
userInfo:nil
repeats:YES];
self.locationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(locationTimerFired:)
userInfo:nil
repeats:YES];
But sometimes these timers are not calling at given time intervals.

You are scheduling a timer in a run loop in the default mode. A Run Loop is basically a queue, scheduling tasks on a certain thread. The exact execution time of your timer depends whether there are already other tasks executing in the same run loop.
It's also possible, that another run loop runs "on top" or modifies the "mode" such that other "tasks" are deferred (e.g a user scrolls). You can read more about run loops and "modes" and their surprising behavior in the official documentation: Anatomy of a Run Loop.
You are also likely not getting a precise timer with NSTimer anyway, since - as already pointed out by #Cyrille - there is that "Timer coalescing" feature of iOS and OSX: Timer Coalescing. You can however implement a quite precise timer using dispatch lib, see a code sample of mine: RXTimer

Related

Why a NSTimer can be delayed in background mode?

My app run in background and uses NSTimer to launch audio after a certain amount of time. Me and my testers have no problems with this, and audio can be launched event after several hours in the background (>10h).
My issue is that some users reports that the audio is often delayed, sometimes by a few minutes, sometimes by an hour.
I do something like that:
UIApplication * app = [UIApplication sharedApplication];
bgTask = [app beginBackgroundTaskWithExpirationHandler:nil];
dispatch_async(dispatch_get_main_queue(), ^{
theTimer = [NSTimer scheduledTimerWithTimeInterval:[theDateIWant timeIntervalSinceNow] target:self selector:#selector(playAudio) userInfo:nil repeats:NO];
});
My questions are :
-Does a NSTimer can be delayed that much by the system ? if so how to go around this problem ?
-How to reproduce this kind of issues ?
- Is it safer to use the following ?
theTimer = [[NSTimer alloc] initWithFireDate:theDateIWant interval:0.0 target:self selector:#selector(playAudio) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:theTimer
forMode:NSDefaultRunLoopMode];
Thanks for any help !
It seems to me that you understand background tasks a little bit wrong.
When the first snippet is performed (I assume that you place it in the applicationDidEnterBackground method), it says the system
I have a small task to perform, please wait for a while till i finish
it!
and the system will wait for about 5-10 minutes before suspend the application.
If you need kind of alarm, you can use LocalNotification to playback specific sound at specified time.

NSTimer-Control flow

When I use NSTimer to call a function on certain time interval as follows:
NSTimer *myTimer =[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(func1) userInfo:nil repeats:YES];
does a background thread gets invoked so as to handle the timer to call func1 every 2 minutes ?
How does the program control flows before & after the NSTimer code section? Does these code segments run on main thread ?
The timer will be called on the main thread (or whatever thread the timer is scheduled on) and this functionality is handled by the run loop.
You can test this with:
- (void)func1
{
NSLog(#"Ping! On main thread=%#", [NSThread isMainThread] ? #"Yes" : #"No");
}
The timer's attached to what's called a run loop. The run loop basically controls a thread's operations (in this case, the main thread). It sleeps the thread until awoken by some sort of trigger, e.g. user input, a timer going off, a system message. Once triggered it dispatches the triggering event to the appropriate place, in this case it will invoke your 'func1'. Once func1 returns back to the run loop, it will look for any other input/triggers, and if there are none, sleeps the thread again.
NSTimers are scheduled on the current thread's run loop. So, whichever thread you call the timer on, callback would happen on that thread only.
For instance, below code fires timer of main thread and ensures func1 is called on main thread.
NSTimer *myTimer =[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(func1) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:myTimer forMode:NSRunLoopCommonModes];
You can utilize NSOperation to enable timer & call back on background thread. You need to schedule the timer on [NSRunLoop currentRunLoop]. Of-course GCD is another alternative for timers.

Why does my NSTimer double decrement my timer?

I have been staring at my code for hours now so I thought I might try coming here for some fresh eyes. I needed to create a timer so I used the code below to do that. The first line is where I create the timer and the second part is my decrementTime method. This is in Objective C for an IOS app. This is my first time posting on StackOverflow (I usually find the answer I am looking for), so please let me know of any unwritten rules that I am not following.
_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(decrementTime) userInfo:nil repeats:YES];
- (void)decrementTime{
self.timeLeft--;
}
I'll add here where I invalidate the first timer
-(IBAction)infoClick:(id)sender{
[_timer invalidate];
}
Then here is info message, where I create another timer
- (void)hideInfoMessage {
_secondTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(decrementTime) userInfo:nil repeats:YES];
}
clarification on my code: the order of events starts with my first code block (creating the timer). Then my second code block is called (invalidating the timer). then finally my third code block is called(making a new timer).
I know it is double incrementing because when I run the code I can visually see the timer double incrementing.
It's pretty easy to invoke the code that creates a timer twice. When you do that you actually have 2 timers running concurrently. Each one will decrement your value, so it will get decremented twice per second.
If you create a timer in your viewWillAppear method, for example, then you need to invalidate it in your viewWillDisappear method so you're sure you only have one running.
The same approach applies to other situations where you create a timer. You need to make sure you balance every call that creates a timer with a call that invalidates that timer.
If you use one of the scheduledTimer... methods, you can save a weak pointer to the timer. The run loop will retain it as long as it's running. When you invalidate it, the system run loop will release it and it will be deallocated. When that happens your weak pointer gets zeroed, so you don't even have to test it to see if it's valid/nil in your viewWillAppear method.
EDIT:
You need to instrument your code. In your infoClick method, is the variable _timer nil? What is it's address?
BTW, the target of an NSTimer is supposed to be a method that takes a single parameter, the timer itself. You should change your decrementTime method to look like this:
- (void) decrementTime: (NSTimer *) timer
{
NSLog(#"In method decrementTime, timer = %X", (unsigned long) timer)
self.timeLeft--;
}
Then look at your log and see if your decrementTime method is being called from 2 different timers (I would bet money that it is.)
You might also want to log the address of the timers you get back from your calls to scheduledTimerWithTimeInterval...

Two NSTimers with one governing the activity of the other?

I've been looking at the thread re: voice detection (http://mobileorchard.com/tutorial-detecting-when-a-user-blows-into-the-mic/).
I'm looking to implement something similar, however, with the following variation.
The levelTimer in that example fires continuously every .03 seconds. I essentially need to wrap another timer around this so that the voice check process only runs for (say) 10 seconds.
I'm finding that having something like:
outerTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(outerTimerFinished:)
userInfo:nil
repeats:NO];
levelTimer = [NSTimer scheduledTimerWithTimeInterval:0.03
target:self
selector:#selector(listenForSounds:)
userInfo:nil
repeats:YES];
…where outerTimerFinished method calls [levelTimer invalidate] isn't working - maybe due to thread blocking?
So can someone help me determine a way for levelTimer to do its work, but only for a specific amount of time?
Thanks.
Put a counter in the listenForSounds: method, and increment it each time the method is called. If you want to run the levelTimer for 10 seconds say, then check for when the counter > 333 and then invalidate the timer.

IOS: Polling for every 2 sec

One feature of my application is to retrieve live data (JSON object) every 2 sec and display it (only while app is in foreground). I can't use push notification service because of server limitations.
Could you please tell me effective way for polling in IOS?
Polling the network wastes battery, but one option may be a recurrent timer:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self
selector:#selector(checkServer:) userInfo:nil repeats:YES];
Where you hit the network in your checkServer: function.
You can use a timer.
poolingTimer = [NSTimer timerWithTimeInterval:pollingPeriod target:self selector:#selector(timerRanOut:) userInfo:nil repeats:YES];
And then in your timerRanOut method you can ask for the new json, parse it, and reload the views.

Resources