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
Related
I am creating a number (~5) NSTimer instances from data stored in a SQLite database using the following code below:
-(void)setupNotificationTimer:(NSDictionary *)timerDetails{
NSLog(#"TIMER REPEATS VALUE: %#", [timerDetails objectForKey:#"repeats"]);
NSLog(#"INTERVAL: %f", [[timerDetails objectForKey:#"interval"] floatValue]);
bool repeat = [[timerDetails objectForKey:#"repeats"] boolValue];
if (repeat) {
NSLog(#"Should Repeat");
}
else{
NSLog(#"Should Not Repeat");
}
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:[[timerDetails objectForKey:#"interval"] floatValue]
target:self
selector:#selector(fireTimer:)
userInfo:timerDetails
repeats:repeat];
[timer fire];
[timers addObject:timer]; //'timers' is a property of the AppDelegate
}
-(void)fireTimer:(NSTimer *)timer {
NSLog(#"Timer Fired");
NSMutableDictionary *dict = [timer userInfo];
[[NSNotificationCenter defaultCenter] postNotificationName:[dict objectForKey:#"notificationName"] object:nil];
}
When I setup these timers the very first time the app loads the timer is setup correctly as the NSLog output below shows but does not repeat despite clearly being set to repeat. Each timer fires once then does not continue.
2013-04-03 11:12:53.541 Test[2635:752b] TIMER REPEATS VALUE: 1
2013-04-03 11:12:53.542 Test[2635:752b] INTERVAL: 10.000000
2013-04-03 11:12:53.543 Test[2635:752b] Should Repeat
2013-04-03 11:12:53.544 Test[2635:752b] Timer Fired
The strange thing happens when I close the app and then re-open it, the timers are created and actually fire repeatedly. This app is also a universal iPhone and iPad app and I only seem to get this behaviour when running the app on iPhone. I really am stumped by this and any help is appreciated.
Edit
As requested the code to cancel the timers:
-(void)viewWillDisappear:(BOOL)animated{
NSArray *viewControllersSet = self.navigationController.viewControllers;
if (viewControllersSet.count > 1 && [viewControllersSet objectAtIndex:viewControllersSet.count-2] == self) {
// View is disappearing because a new view controller was pushed onto the stack
NSLog(#"New view controller was pushed");
} else if ([viewControllersSet indexOfObject:self] == NSNotFound) {
// View is disappearing because it was popped from the stack
NSLog(#"View controller was popped");
for(int i = 0; i < [[appDelegate timers] count]; i ++){
NSTimer *timer = [[appDelegate timers] objectAtIndex:i];
[timer invalidate];
timer = nil;
}
[[appDelegate timers] removeAllObjects];
}
}
If an NSTimer does not fire after being created with scheduledTimerWithTimeInterval, it usually means that it has not been attached to the correct runloop. Could it be that your method setupNotificationTimer() is called with different runloops during initialization and when returning to the foreground? (i.e. in the context of a different thread or similar)
This code would work around the problem:
NSTimer* timer = [NSTimer timerWithTimeInterval:[[timerDetails objectForKey:#"interval"] floatValue]
target:self
selector:#selector(fireTimer:)
userInfo:timerDetails
repeats:repeat];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
This ensures that your timers are scheduled on the main runloop and will fire.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
NSTimer doesn't stop
I am using a NSTimer to update the value of a slider while a audio is playing.
if (sliderUpdateTimer) {
[sliderUpdateTimer invalidate];
sliderUpdateTimer=nil;
}
sliderUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(updateSliderForAudio) userInfo:nil repeats:YES];
once the audio is finished playing i am invalidating the Timer in audioPlayer delegate method.
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
NSLog(#"audioPlayerDidFinishPlaying");
[player release];
audioPlayer=nil;
[sliderUpdateTimer invalidate];
[sliderUpdateTimer release];
sliderUpdateTimer=nil;
}
The delegate method is getting called but the timer is not stopping.... I have only once thread on which i am runing the timer. But still the timer does not stop.... Any help in this regard is welcome...
First of all, you would be over releasing it and should receive EXC_BADACCESS (you're not retaining the timer when you set it, so you shouldn't release it either). Should only call:
[sliderUpdateTimer invalidate];
sliderUpdateTimer=nil;
Since you don't receive a crash, it seems sliderUpdateTimer is nil when calling audioPlayerDidFinishPlaying:. User NSLog or put a breakpoint to check if this is true. If this is the case, you're probably setting it to nil at some point, search for slideUpdateTimer and check where this might occur.
Hey try this thing...
- (void)stopTimer{
[sliderUpdateTimer invalidate];
//don't release it. as it is autoreleased.
//[sliderUpdateTimer release];
sliderUpdateTimer=nil;
}
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
NSLog(#"audioPlayerDidFinishPlaying");
[player release];
audioPlayer=nil;
[self performSelectorOnMainThread:#selector(stopTimer) withObject:nil waitUntilDone:NO];
}
I know you have only single thread but there may be two different run loops
Try after removing the below lines from 1st section of your code
if (sliderUpdateTimer){
[sliderUpdateTimer invalidate];
sliderUpdateTimer=nil;
}
Don't need to call release message on that sliderUpdateTimer object
Because you have created that object with pending autorelease message
Here no need to use these lines.
I hope this will work.
I have a timer for my project, each time it decrements by 1 second. But if the counter starts working for the second time it gets decremented by 2 seconds and for the third time by 3 seconds etc. what should I do to get 1 sec decrement for all the time?
-(void)viewDidAppear:(BOOL)animated {
count=15; //timer set as 15 seconds
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateCounter:)userInfo:nil repeats:YES]; //for decrementing timer
}
- (void)updateCounter:(NSTimer *)theTimer {
count -= 1;
NSString *s = [[NSString alloc] initWithFormat:#"%d", count];
if(count==0) // alert for time expiry
{
alert = [[UIAlertView alloc] initWithTitle:#"Time Out!!" message:#"Your time is expired." delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
self.timer.hidden=YES;
[self dismissModalViewControllerAnimated:YES];
}
else {
self.timer.text = s;
}
[s release];
}
From what you've posted, you're creating multiple timers and not stopping any of them. So after 3 times, you have 3 timers firing each second.
At a minimum, when the timer hits zero you want to invalidate it:
[theTimer invalidate]
But you may also want to consider holding onto the timer you create (in a #property) so that you can invalidate and release it if the user leaves this view some other way before your counter actually goes to zero.
Hope that helps.
EDIT
(Just as I posted this I notice that you have know accepted the above answer and seemed to have removed your 'its not working' comment for the answer from #Firoze Lafeer. But I will leave this here any way.)
Running the code below as a test I do not get your issue. And even when I did not invalidate I just got multiple outputs in the log.
Instead of seeing what is going on by looking at what the textfield in the app is doing. Try and use logging as I have below to see if that gives you a better idea of what is happening.
-(IBAction)runTimer:(id)sender { // attached to a button
[self invalidateTimer];
count=15; //timer set as 15 seconds
//for decrementing timer
countimer = [NSTimer scheduledTimerWithTimeInterval:1.0f
target:self
selector:#selector(updateCounter:)
userInfo:nil
repeats:YES];
}
-(void)updateCounter:(NSTimer *)theTimer {
count -= 1;
NSLog (#"count =#%d", count);
if(count==0) // alert for time expiry
{
[self invalidateTimer];
}
}
-(void)invalidateTimer {
if ([countimer isValid]) {
[countimer invalidate];
NSLog(#"countimer invalidated ");
countimer = nil;
}
}
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.
I have a Class with a NSTimer *myTimer; variable. At some point I do:
myTimer = [NSTimer scheduledTimerWithTimeInterval:20 target:self selector:#selector(doStuff) userInfo:nil repeats: YES];
further, I have a method:
- (void)doStuff
{
if(myTimer)
{
//do stuff
}
}
and I stop my timer when the class is released through:
- (void)dealloc
{
if (myTimer) { //if myTimer==nil it already has been stopped in the same way
[myTimer invalidate];
myTimer = nil;
}
}
Now, the problem is that when I release the class the timer goes on and on and on firing the event anyway. Am I doing something wrong? It seems the dealloc method is never called, otherwise myTimer would be nil and even if the selector is fired it would not go into the if(myTimer)
This will never work, because timers retain their target, which means your dealloc method will never get invoked until after you've invalidated the timer.
For more info, see the NSTimer documentation and this blog post on "Dangerous Cocoa Calls"
Have you tried the handy debugger tools at your disposal? What happens if you set a breakpoint in your dealloc method? Also, you should post more context around your creation. Is it possible you're creating the timer more than once, thereby replacing the original (but not invalidating it) and leaving it out there to fire at will?