How do I check if an NSTimer is running? - iphone

I have an IBAction where upon the button being pressed, it creates an unscheduled timer. Then, if that same timer has already started, //do something, else start the timer which was created.
Here is what I have so far:
- (IBAction)button1Press {
NSMethodSignature *sgn = [self methodSignatureForSelector:#selector(onTick:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sgn];
[inv setTarget: self];
[inv setSelector:#selector(onTick:)];
NSTimer *tapTimer = [NSTimer timerWithTimeInterval: 1.0
invocation:inv
repeats:NO];
if (/*tapTimer is running*/) {//do something
}else{
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer: tapTimer forMode: NSDefaultRunLoopMode];
}
}
My problem is what to put as the condition. If I put tapTimer isValid or != nil then it always returns true, because tapTimer is already declared. I do not want to invalidate or nil out the timer because the main purpose of the button is to only do the action if the button is pressed twice in a time interval of 1 second.
If there is a completely different approach to do what I want then please do tell!
Thanks loads!

From what I understand so far, I see that you are attempting to check whether tapTimer is running or not. I have one suggestion. Use a variable for indicating whether you are having tapTimer running or not. When you run the timer, you change this variable to true, and when the timer's time hits 0 and invoke the method you have selected, you change this variable to false in this method.
Does this help?

I would recommend using a nil check to determine if your timer is running or not.
...
//Define _tapTimer in .h
if (_tapTimer) {//do something
}
else{
_tapTimer = [[NSTimer timerWithTimeInterval: 1.0
invocation:inv
repeats:NO] retain];
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer: _tapTimer forMode: NSDefaultRunLoopMode];
}
...
-(void)timerFired:(NSTimer*)timer
{
if(timer == _tapTimer)
{
//Handle timerfired
[_tapTimer release], _tapTimer = nil;
}
}

You can query the RunLoop to know if there is such a timer inside the loop
CFRunLoopRef loopRef = [[runner currentRunLoop] getCFRunLoop];
Boolean timerAdded = CFRunLoopContainsTimer(loopRef, (CFRunLoopTimerRef)timer ,kCFRunLoopDefaultMode)
if (timerAdded)
{
...
}
but, I haven't tested that code yet

Related

NSTimer some times getting fired before the fire date?

I'm setting up a timer to fire at an interval of 13 mins. But the timer seems to be getting fired at irregular intervals. This is an intermittent issue. Some users reported the issue and I'm not able to reproduce the same.
- (void)resetIdleTimer
{
if (_idleTimer)
{
[_idleTimer invalidate];
[_idleTimer release];
_idleTimer = nil;
}
_idleTimer = [[NSTimer scheduledTimerWithTimeInterval:13*60.0
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO] retain];
}
- (void)idleTimerExceeded
{
// do some processing
[_idleTimer release];
_idleTimer = nil;
}
Timer will be reset (resetIdleTimer) depending on some conditions, but that's anyhow resets the timer with 13 mins.
When I looked into code, I can see only the issue that is not passing timer parameter to the selector. I'm not sure whether that's the reason for this issue or not ? Did anyone come across this kind of weird ness ? (Any how I will be updating the code to to have timer as an argument).
One user reported that its just happened after 4 mins itself.
Let confirm one thing,not passing timer parameter to the selector will not cause any issues.
Here is the updated code which works fine
#property(nonatomic,strong)NSTimer *myTimer;
or
#property(nonatomic,retain)NSTimer *myTimer;
Methods
- (void)resetIdleTimer
{
if ([self.myTimer isValid])
[self.myTimer invalidate];
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:13*60.0
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO];
}
- (void)idleTimerExceeded
{
NSLog(#"tiggered");
}
i don't find any error in your code, the only idea about is that a user enter your class creating an instance, let's call it instance "A", then start the timer, then exit your class before 13 minutes, then enter again with a new instance "B", a new timer starts...
at this point user expects your timer methods fires after 13 minutes... but timer of "A" is still pending (because there may be an error in the way you close your class) and executes...
You may try to add some logs and try to do as i said, and check all logs (in particular if you enter the dealloc, and that the log in idleTimerExceeded print instances equals to the last instances printed in resetIdleTimer):
-(void)resetIdleTimer {
if (_idleTimer)
{
[_idleTimer invalidate];
[_idleTimer release];
_idleTimer = nil;
}
NSLog(#"...resetIdleTimer: instance: %p", self);
_idleTimer = [[NSTimer scheduledTimerWithTimeInterval:13*60.0
target:self
selector:#selector(idleTimerExceeded)
userInfo:nil
repeats:NO] retain];
NSLog(#"...resetIdleTimer: _idleTimer: %p", _idleTimer);
}
- (void)idleTimerExceeded
{
// do some processing
NSLog(#"...idleTimerExceeded: instance: %p", self);
NSLog(#"...idleTimerExceeded: _idleTimer: %p", _idleTimer);
[_idleTimer release];
_idleTimer = nil;
}
-(void) dealloc
{
NSLog(#"...dealloc: instance: %p", self);
[super dealloc];
}
just an idea... but you may have forgotten that timers retain the instance passed in target:parameter (you pass "self", so your class instance is retained), so a common error is to forget to close the timer when closing the class instance.
a correct way should be:
-a root class (AAA) instantiate your class (BBB) with timer
-BBB start its timer
-AAA wanna dealloc/resetIdleTimer class BBB: before to release it must stop/invalidate BBB.timer (if not timer retain BBB, and BBB won't be released until timer fires/executes its method)
so, if it's your case... add a method that could be called from class AAA that stop/invalidate the timer, something like:
-(void)stopTimer{
if (_idleTimer)
{
[_idleTimer invalidate];
[_idleTimer release];
_idleTimer = nil;
}
}
If you want your timer to fire in exact 13 minutes, maybe this will help:
https://stackoverflow.com/a/3519913/1226304

how can i perform selector after time without AfterDelay ?! (code unside)

first of all thanks for getting into my questions.
now, i tried a lot of different ways to call some function in my code but the ways i find it works (performselector:withobject:afterdelay) are get into the function immediately and i need the function to make some "if's" after random times.
Well you can see the code in here-
-(void)playGame{
isButtonPressed=NO;
int minimum=1;
int maximum=4;
int randomTime=(arc4random()%(maximum-minimum))+minimum;
[self performSelector:#selector(ifButtonNotPressed) withObject:nil afterDelay:randomTime];
}
-(void)closingAction{
NSLog(#"Closing Panel automatic");
if (randomFrog==1){
[self performSelector:#selector(animatePanelCloseAction::) withObject:gameImage1_up withObject:gameImage1_down];
}
[self performSelector:#selector(playGame) withObject:nil afterDelay:0.25];
}
-(IBAction)buttonAction:(id)sender{
if (sender == gameButton1 && randomFrog==1){
[self performSelector:#selector(disableAllButtons)];
score=score+1;
isButtonPressed=YES;
[scoreLabel setText:[NSString stringWithFormat:#"Score:%d",score]];
[self performSelector:#selector(closingAction)];
}
}
-(void)ifButtonNotPressed{
if (isButtonPressed==NO){
[self performSelector:#selector(closingAction)];
}
}
as you can see, im trying to check if button was pressed, but its going inside and check it immeditlly and perform it after delay.
how can i call it after real delay ?!
thank you all.
amir.
Try NSTimer to fix this issue.
if(callTimer)
{
[callTimer invalidate];
callTimer=nil;
}
callTimer=[NSTimer scheduledTimerWithTimeInterval:0.50 target:self selector:#selector(callFunction) userInfo:nil repeats:NO];
with help of this you can call any function at particular time interval. if function call again then it invalidates current timer and create new timer for that.
Dont forgot to invalidate timer after calling function means invalidate timer in particular function.
Hey you can use NSTimer class for the same.
- (void)FireTimer : (NSInteger) afterTimeInterval
{
if (afterTimeInterval > 0) //afterTimeInterval is in seconds
{
[NSTimer scheduledTimerWithTimeInterval: afterTimeInterval
target: self
selector: #selector(ResetListAfterExpiration:)
userInfo: nil
repeats: NO];
}
}
- (void)ResetListAfterExpiration: (NSTimer *)timer
{
// your actions
… … …
[timer invalidate]; // must be done to prevent memory leak
}
//Now You call the FireTimer method from where ever you want and it will wait for afterTimeInterval seconds and call the method ResetListAfterExpiration. That's your answer.

(iphone) do I need to invalidate timer when repeats: no?

[NSTimer scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:NO];
When repeats: is set to NO, do I need to invalidate the timer inside the specified selector?
Thank you
Edit
Another question, if it self invalidates,
How do you properly cancel a such timer?
Since invalidating already-invalidated timer would crash I assume?
maintain a pointer to the timer and set it to nil inside the selector that will get fired?
No, the timer will invalidate itself
#Eugene if you are using
[NSTimer scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:YES];
then in the selector method you need to give a function like this one
- (void)timerFireMethod:(NSTimer*)theTimer
so when you want to invalidate it you can have a condition like this one
if(workDone == YES)
{
[theTimer invalidate];
}
But if you are using NO in the repeat option then the timer will invalidate itself.
You can maintain flag to save whether the timer has been fired or not.
eg.
BOOL gameOver = NO;
NSTimer * gameOverTimer;
-(void)startGame
{
gameOverTimer = [NSTimer scheduledTimerWithTimeInterval:600 target:self selector:#selector(stopLevel:) userInfo:nil repeats:NO];
// your code
}
-(void)stopLevel:(id)sender
{
gameOver = YES;
// your code
}
-(void)levelFinishedSuccesfully
{
// this method will get called if user finishes the level before your timer ends/stops the level. So the timer is valid and we need to invalidate it
if(!gameOver)
{
[gameOverTimer invalidate];
gameOverTimer = nil;
}
// your code
}
Hope this helps.
If repeats is YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.
you are missing to add the timer source to your runloop
addTimer:forMode:

NStimer and for loop

I'm struggling with this. I saw some code where you can do this :
- (void)startTimer {
pauseTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(doActions) userInfo:nil repeats:YES];
}
Then call it in doActions.
Problem is i want to call it while a button is being pressed, and do actions is an IBaction. I keep getting a sigAbrt.
Can someone give me some sample code where you cay change a label from 'on' to 'off' every 1 second while a button is being pressed?
EDIT
i mean if doActions looks like this
- (IBAction) doActions {
for(int j; j<100; j++){
theLabel.hidden != theLabel.hidden;
//Does invalidate timer go here?
}
}
It still isn't clear to me, what you're trying to accomplish: I would have found it much easier, if you simply had it written down in plain english, after all.
That said, here's what I think will get you where you want to go:
// Assuming ivars:
// NSTimer* toggleTimer
// NSUInteger toggleCount
- (void)toggleTimerFired:(NSTimer*)timer
{
if ( toggleCount++ >= 100 ) {
[self stopToggling:self];
return;
}
// do whatever you need to do a hundred times here
}
- (IBAction)stopToggling:(id)sender
{
[toggleTimer invalidate], toggleTimer = nil; // you don't want dangling pointers...
// perform any other needed house-keeping here
}
- (IBAction)startToggling:(id)sender
{
[self stopToggling:self]; // if `startToggling:` will NEVER be called when a timer exists, this line CAN be omitted.
toggleCount = 0;
toggleTimer = [NSTimer scheduledTimerWithTimeInterval:1. target:self selector:#selector(toggleTimerFired:) userInfo:nil repeats:YES];
}
Depending on what exactly you want to do, startToggling: needs to be sent on either touchUpInside or touchDown. In the second case, stopToggling: probably needs to be called on any touchUp... event of the button.
- (void)startTimer {
pauseTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(doActions) userInfo:nil repeats:YES];
}
- (void) doActions {
theLabel.hidden != theLabel.hidden;
}

how to stop a timer triggered runloop?

if i set up a runloop like that:
NSRunloop* loop = [NSRunloop currentRunLoop];
[runLoop addTimer:anyTimer forMode:NSDefaultRunLoopMode];
can i stop it again ?
or is the only way to ignore the notification i use to trigger the further action ?
ok, i give an example for the problem:
-(void)blinkeffekt:(double)pollingTime{
NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
if (pollingTime != 0) {
NSTimeInterval interval =(double)pollingTime / 1000;
NSTimer timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:#selector(polling) userInfo:nil repeats:YES];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
}
else {
[timer invalidate];
}
}
surely, here are several errors - no question. but i think, it shows my problem. and this is not really solvable with the answers until now.
i need to run a timer and stop it later. and ideally out of another function in the class.
but then i cannot access "timer" anymore and runloop doesnt allow to figure out, if such a "message" is available.
and it would be extremely ineffective, if i would register for each calling of the function a new timer.
You need to send a invalidate message to the timer to remove it from the RunLoop.
See Doc
[anyTimer invalidate];