How to exit NSTimer? [duplicate] - objective-c

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].

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.

iphone - NSTimers in background

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.

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

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.