NSTimer is it thread safe? - objective-c

I have a repeated timer with interval of 1/4 second. I am initializing it like this:
[NSTimer scheduledTimerWithTimeInterval:0.25
target:self
selector:#selector(toggleCams)
userInfo:nil
repeats:YES];
Does this happen synchronously? In other words, am I guaranteed that another method in the same class will not be called at the same time toggleCams is called?

The NSTimers actually just periodically fire events into the enclosing NSRunLoop, which each thread has (or should have). So, if you have a child (or background) process running in a different thread, the NSTimers will fire against that thread's NSRunLoop instead of the application's main NSRunLoop.

Related

When Does NSTimer Fire If Main Thread Is Busy?

When does an NSTimer fire if the main thread is busy? Here is the line of code in question:
[NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(onTimer:)
userInfo:nil
repeats:YES];
I'm a bit new to NSTimer (and run loops and threads) so thank you for the help. My app does a refresh on an NSTimer that was scheduled on the main thread. There may be issues if the refresh timer fires at the exact time interval specified and does not wait for the main thread to free up.
An example would be if the user does some action on the screen that is linked to data, and then the refresh fires immediately after and changes that data, causing the app to crash. Is this likely to happen, or will the refresh not fire until the users action has been resolved on the main thread?
NSTimer schedules the timer on the current run loop (i.e. main run loop in this example) and so if the main thread is busy, the timer won't fire until you yield back to the main run loop.
See Timer Programming Topics and the NSTimer Class Reference, for more information.

NSTimer is pausing a while when UIGesture action performed

NSTimer is pausing a while an then again it is starting when UIGesture action performed.
I am developing an iOS application where I have to trigger the NSTimer repeatedly for every 0.5 seconds and based on that timer I'm moving a song progress bar. And also handling touch events when user touching on some images which I kept on the same view. I am seeing when user touches the images, the timer for moving the progress bar is stopping a little while and continues again.
Are there any problem that we can't handle timer and touch events at the same time in the same view?
My code for timer
myTimer = [NSTimer scheduledTimerWithTimeInterval:timeinterval target:self selector:#selector(updatplayer:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:myTimer forMode:NSRunLoopCommonModes];
This is a common issue with NSTimer. While you can play with run loop modes to improve things, a nicer solution is to switch to GCD timers. See RNTimer for an example. See the docs on dispatch_source_create for more information.

NSTimer not firing repeatedly when told

The following only executes once. Why is it, when repeat is set to YES?
NSTimer *timer = [NSTimer timerWithTimeInterval:1/3 target:self
selector:#selector(updateThingsRepeatedly:) userInfo:nil repeats:YES];
[timer fire];
If i understand things right, updateThingsRepeatedly method should be called every 1/3 of a second repeatedly. Is this not correct?
It does call the method once, however, but does not start a loop, as i would expect.
1/3 evaluates to 0. Replace it with 1.0/3.0 and you'll be set.
It's the C way to work with numbers. You have to hint the compiler about the desired type of an expression. 1/3 is an integer division, and its result is an integer. 1.0/3.0 is double, 1.0f/3.0f is float. You can achieve the same effect with a cast: (double)1/(double)3.
NSTimer can be set to fire after 0 seconds, which means it will fire once at the end of the run loop.
You can use this method -
[NSTimer scheduledTimerWithTimeInterval:1.0/3.0 target:self selector:#selector(updateThings:) userInfo:nil repeats:YES];
and if you are using
[NSTimer timerWithTimeInterval:1.0/3.0 target:self
selector:#selector(updateThings:) userInfo:nil repeats:YES];
then you must add it to a run loop.
Discussion
You must add the new timer to a run loop, using addTimer:forMode:. Then, after seconds have elapsed, the timer fires, invoking invocation. (If the timer is configured to repeat, there is no need to subsequently re-add the timer to the run loop.)
to add on a run loop use -
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(updateThings:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
Use -scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:, and do not call -fire.
And use floating point numbers for your time interval. I.e., 1.0/3.0.
You have to add it to the run loop:
Scheduling Timers in Run Loops
A timer object can be registered in
only one run loop at a time, although it can be added to multiple run
loop modes within that run loop. There are three ways to create a
timer:
1) Use the scheduledTimerWithTimeInterval:invocation:repeats: or
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: class
method to create the timer and schedule it on the current run loop in
the default mode.
2) Use the timerWithTimeInterval:invocation:repeats: or
timerWithTimeInterval:target:selector:userInfo:repeats: class method
to create the timer object without scheduling it on a run loop. (After
creating it, you must add the timer to a run loop manually by calling
the addTimer:forMode: method of the corresponding NSRunLoop object.)
3) Allocate the timer and initialize it using the
initWithFireDate:interval:target:selector:userInfo:repeats: method.
(After creating it, you must add the timer to a run loop manually by
calling the addTimer:forMode: method of the corresponding NSRunLoop
object.)
Once scheduled on a run loop, the timer fires at the
specified interval until it is invalidated. A non-repeating timer
invalidates itself immediately after it fires. However, for a
repeating timer, you must invalidate the timer object yourself by
calling its invalidate method. Calling this method requests the
removal of the timer from the current run loop; as a result, you
should always call the invalidate method from the same thread on which
the timer was installed. Invalidating the timer immediately disables
it so that it no longer affects the run loop. The run loop then
removes and releases the timer, either just before the invalidate
method returns or at some later point. Once invalidated, timer objects
cannot be reused.
and, as Costique spotted (+1), you will have to specify an appropriate time interval.
then you should be all set.
You haven't scheduled this timer with the runloop. The reason it fires is because you manually fired it. You don't need to do that, in general, but you do need to schedule it with the runloop. You can use the convenience method to automatically schedule the timer with the runloop:
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1./3 target:self selector: #selector(updateThings:) userInfo:nil repeats:YES];
replace 1/3 by 1.0/3.0. Also use addTimer:forMode: to a run loop

How to exit NSTimer? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
NSTimer doesn't stop
I am using [NSThread exit] to leave the NSTimer thread.
It stops the application entirely.
How do you stop or exit NSTimer thread?
[timer invalidate];
timer = nil;
If you want to definitely stop a NSTimer you may use
[timer invalidate];
However, if you want to temporary prevent the timer to fire you may consider using
[timer setFireDate:[NSDate distantFuture]];
From Apple Documentation:
Once scheduled on a run loop, the timer fires at the specified interval until it is invalidated. A non-repeating timer invalidates itself immediately after it fires. However, for a repeating timer, you must invalidate the timer object yourself by calling its invalidate method. Calling this method requests the removal of the timer from the current run loop; as a result, you should always call the invalidate method from the same thread on which the timer was installed. Invalidating the timer immediately disables it so that it no longer affects the run loop. The run loop then removes and releases the timer, either just before the invalidate method returns or at some later point. Once invalidated, timer objects cannot be reused.
You should double check to make sure your timer is valid before invalidating it.
if ([timer isValid]) {
[timer invalidate];
timer = nil;
}
I am not sure what you mean by NSTimer thread, but to stop NSTimer instance you scheduled, you can call [NSTimer invalidate].

NSTimer: Getting firing to NOT act retroactively

I'm currently using the snippet of code presented below to fire some methods every second. My app is running in the background. The problem is that if the computer wakes up after a sleep period the timer wants to retroactively fire all the methods it has missed. Similar issues come up if the user were to change the System Clock time.
Basically I want to implement the proper timer method that will have my methods called only every current second. If a second (or minute or hour or day) has passed and for whatever reason the methods weren't called I want my app to just continue from the current moment in time.
Also, can we keep this while using NSTimer?
Thanks!
-(void)start
{
NSTimer * timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(tasks:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode: NSDefaultRunLoopMode];
}
To handle the big time changes you can use the link UIApplicationSignificantTimeChangeNotification and unregister/reregister your timer.
To deal with the sleep issue, you can unregister and then reregister your timer whenever the machine goes to sleep and wakes up. See this technical note for information on how to do that. This solution won't work for changing the system time, though.