NSTimer not firing repeatedly when told - objective-c

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

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 and NSRunLoop

My app tracks a user with CLLocationManager. In the delegate call didUpdateToLocation I do all the fun stuff of saving their position. However, I needed a way to test if they had stopped. That away I could stop recording locations and consider their trip over. So I have a NSTimer in CCLocationManager that gets added and removed every time didUpdateToLocation is called. That away it will be initiated when the user stops and CLLocationManager stops getting called.
The only way that I could ever get the NSTimer to work is to do:
[[NSRunLoop mainRunLoop] addTimer:userStoppedMovingTimer forMode:NSRunLoopCommonModes];
Then to remove it:
[userStoppedMovingTimer invalidate];
I've never had to add timers like this in the past. Could someone shed some light as to why this is?
From the documentation:
There are three ways to create a timer:
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.
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.)
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.)
You were probably using option 1 previously, and now you're using option 2 or 3.

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 is it thread safe?

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.

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.