I am starting a timer to trigger an event based on a false condition.However if the condition becomes true within the scheduled time, i need to stop the event from triggering.
if(!condition)
{
myTimer =[NSTimer scheduledTimerWithTimeInterval:200 target:self selector:#selector(trigger:) userInfo:nil repeats:NO];
}
else
{
if([myTimer isvalid])
[myTimer invalidate]
myTimer= nil;
}
If condition becomes true within 200 sec don't trigger the #selector.
Invalidating the timer doesn't stop the method from triggering. I can do this by having a BOOL flag, but can i do it using NSTimer's methods.
You must be creating multiple instance of myTimer. Check whether myTimer already initailized or not ?
if(!condition)
{
if(myTimer == nil)
myTimer =[NSTimer scheduledTimerWithTimeInterval:200 target:self selector:#selector(trigger:) userInfo:nil repeats:NO];
}
else
{
if([myTimer isvalid])
{
[myTimer invalidate]
myTimer = nil;
}
}
Related
A strange situation:
If I started my Timer again and again without stopping it first, it will count increasingly fast. I guess it is because it starts multiple timers now?
However, when I finally want to stop it, it cannot be stopped...keep going forever.
(Maybe for design consideration, I should disable users from pressing start again, but I'm wondering what is really behind this and why the timer can't be stopped.)
- (IBAction)Start:(id)sender {
countInt = 0;
self.Time.text = [NSString stringWithFormat:#"%i", countInt];
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countTimer) userInfo:nil repeats:YES];
}
- (IBAction)Stop:(id)sender {
[timer invalidate];
}
- (void) countTimer {
countInt += 1;
self.Time.text = [NSString stringWithFormat:#"%i", countInt];
}
#end
The simple solution is to call stop at the beginning of the start method.
Note that in stop you should also set timer = nil;
Assuming there is a property timer
#property NSTimer *timer;
the most reliable way to start and stop the timer only once respectively is to create two methods.
- (void)startTimer
{
if (self.timer == nil) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(countTimer)
userInfo:nil
repeats:YES];
}
}
- (void)stopTimer
{
if (self.timer != nil) {
[self.timer invalidate];
self.timer = nil;
}
}
Both methods perform a check, so the timer can't be restarted while it's running and vice versa.
Now just call the methods in the start/stop IBActions (the names should start with a lowercase letter).
- (IBAction)Start:(id)sender {
countInt = 0;
self.Time.text = [NSString stringWithFormat:#"%i", countInt];
[self startTimer];
}
- (IBAction)Stop:(id)sender {
[self stopTimer];
}
The benefit is pressing Start has no effect when the timer is already running.
When you hit 'start' multiple times you are creating multiple timers. So you are getting multiple timers firing and executing your timer callback. In this timer callback you increment counters. Since there are many timers now, they are all incrementing your counter, hence explaining your rapid increase in the counter.
You can allow the user to tap Start twice, as long you can define what happens when you hit Start while the timer is already going. But you definitely need to invalidate the old timer before creating a new one.
- (IBAction)Start:(id)sender {
...
// Stop previous timer before creating a new timer.
if (timer != nil) {
[timer invalidate]
}
...
}
I have an NSTimer that I want to be stopped when I leave my vViewVontroller:
The timer is in a method that I call from viewWillAppear :
- (void) myMehtod
{
//timer = [[NSTimer alloc] init];
// appel de la methode chaque 10 secondes.
timer = [NSTimer scheduledTimerWithTimeInterval:10.0f
target:self selector:#selector(AnotherMethod) userInfo:nil repeats:YES];
//self.timerUsed = timer;
}
I call the method stopTimer in viewWillDisappear
- (void) stopTimer
{
[timer invalidate];
timer = nil;
}
PS: I tried the answer of user1045302 in this question but it didn't work:
How to stop NSTimer
The source of the problem probably is that myMehtod is called twice or more times.
Since the method does not invalidate existing timers before setting up the new one you actually have several timers ticking at the same time.
Fix is easy: invalidate old timers before setting up a new one:
- (void)myMehtod
{
[timer invalidate];
timer = [NSTimer scheduledTimerWithTimeInterval:10.0f
target:self
selector:#selector(anotherMethod)
userInfo:nil
repeats:YES];
}
I am getting this exception in the console:
Error:
2015-06-25 23:12:01.841 Copyfeed for Mac[9512:584232]
-[_NSViewLayoutAux invalidate]: unrecognized selector sent to instance 0x6000001657c0
when checking if my timers are valid/and when invalidating them.
if ([_staticTimer isValid]) {
[_staticTimer invalidate];
_selectionTimer =
[NSTimer scheduledTimerWithTimeInterval:2 target:self
selector:#selector(hideHUD) userInfo:nil repeats:NO];
}
if ([_selectionTimer isValid]) {
[_selectionTimer invalidate];
_selectionTimer =
[NSTimer scheduledTimerWithTimeInterval:2 target:self
selector:#selector(hideHUD) userInfo:nil repeats:NO];
}
This is my new code:
if (_selectionTimer != nil) {
[_selectionTimer invalidate];
_selectionTimer = nil;
_selectionTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(hideHUD) userInfo:nil repeats:NO];
}
if (_staticTimer != nil) {
[_staticTimer invalidate];
_staticTimer = nil;
_selectionTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(hideHUD) userInfo:nil repeats:NO];
}
#property (strong )NSTimer *staticTimer;
#property (strong )NSTimer *selectionTimer;
Now getting this error when I debug with zombie objects on.
2015-06-26 00:39:45.523 Copyfeed for Mac[11191:824502] ***
-[CFRunLoopTimer release]: message sent to deallocated instance 0x608000175e40
There are a couple of potential issues here.
if (_staticTimer != nil) {
[_staticTimer invalidate];
_staticTimer = nil;
_selectionTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(hideHUD) userInfo:nil repeats:NO];
}
What you are doing here is overriding the selectionTimer even though it might still contain a timer that is still scheduled in the run loop. So if you reset the property here, you should also make sure to call [_selectionTimer invalidate] before doing so.
Depending on what you are doing when the timer is firing, this could explain the crash on CFRunLoopTimer.
A general advice that turned out to be very helpful for me when working with NSTimer: I would recommend declaring all the properties that hold a scheduled timer as weak as they are retained by the run loop anyway. This way, you don't need to explicitly set them to nil after invalidating them but instead you can simply call invalidate every time you want to get rid of it and it will also automatically become nil once it has fired by the run loop, releasing all the data it might hold on to.
Note that this would still require you to call invalidate in case you want to cancel a timer or before replacing one, but you no longer need to set it to nil after doing so.
After you invalidate it, you should set an NSTimer object to nil.
invalidate method also does a release.
If you didn't do that, calling a method on it like isValid could cause your crash.
if (_selectionTimer != nil) {
[_selectionTimer invalidate];
_selectionTimer = nil;
// do something
}
Check here
if (_staticTimer != nil) {
[_staticTimer invalidate];
_staticTimer = nil;
//_selectionTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(hideHUD) userInfo:nil repeats:NO];
_staticTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(hideHUD) userInfo:nil repeats:NO];
}
From the documentation:
Because the run loop maintains the timer, from the perspective of
memory management there's typically no need to keep a reference to a
timer after you’ve scheduled it. Since the timer is passed as an
argument when you specify its method as a selector, you can invalidate
a repeating timer when appropriate within that method. In many
situations, however, you also want the option of invalidating the
timer—perhaps even before it starts. In this case, you do need to keep
a reference to the timer, so that you can send it an invalidate
message whenever appropriate. If you create an unscheduled timer (see
“Unscheduled Timers”), then you must maintain a strong reference to
the timer (in a reference-counted environment, you retain it) so that
it is not deallocated before you use it.
So you should make the timer weak instead of strong
Im making an iphone game that uses NStimer for movement. I found out that there is a bug that makes the timers stack some times, but i havnt found out what causes is. Is there any way to see how many timers are allocated and is there any way to prevent it by doing something like this:
If(myTimer.numberOfAllocatedTimers == 0) {
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:#selector(updateme) userInfo:nil repeats:YES];
}
Try this:
// use this to start timer
myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(doSthing) userInfo:nil repeats:YES];
// use this to stop timer
if (myTimer) {
[myTimer invalidate];
myTimer = nil;
}
A little more context on where this code is being used would be helpful.
However, I'll try to explain why it's happening anyway.
Every time you use [NSTimer scheduledTimerWithTimeInterval...], a new timer is created, and will start repeating. If your code creates a timer inside the callback method, see example below, you'll stack timers.
- (void)updateme {
if ([NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:#selector(updateme) userInfo:nil repeats:YES]) {
// This creates stacked timers!
}
}
Here's how you should do it:
#property (nonatomic, strong) NSTimer *myTimer;
...
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:#selector(updateme:) userInfo:nil repeats:YES];
...
- (void)updateme:(NSTimer *)timer {
if (timer == self.myTimer) {
// Do something because the timers are equal
}
}
I have a void function which just have NSLog(#"Call me"); in its body.
I call it in my view in every ten seconds by using
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(yourMethod) userInfo:nil repeats:YES];
But I want it to stop it after 5 iterations. However it goes to infinity. How can I do that?
1) Keep a global variable, that increments from 0 to 5.
int i = 0;
2) Incement this variable inside your timer function..
-(void) yourFunction:(NSTimer*)timer{
//do your action
i++;
if(i == 5){
[timer invalidate];
}
}
3) When creating timer
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:10
target:self
selector:#selector(yourMethod:) // <== see the ':', indicates your function takes an argument
userInfo:nil
repeats:YES];
You should take one counter, increment it every time when your method get called, count for 5 and then invalidate your timer using below code.
[timer invalidate];
To destroy the timer from the current loop, you should call [timer invalidate];
To determine five occurrences, you need to maintain a variable and increment its count each time. If it is equal to 5, call invalidate method.
First of all you need to declare an int and declare your NSTimer *timer, so we can stop it:
#interface AppDelegate : UIViewController {
int myInt;
NSTimer *timer;
}
To start the NSTimer you'll need to change just a bit of the code:
timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(yourMethod) userInfo:nil repeats:YES];
Inside your void function you can make the verification to check if the code's running after 5 iterations:
- (void)myVoid{
NSLog(#"Call Me");
if (myInt == 5) {
[timer invalidate];
timer = nil;
}
myInt++;
}