9

I have the following code in my .h file:

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <AVFoundation/AVFoundation.h>
#import <MapKit/MapKit.h>

@interface LandingController : UIViewController<CLLocationManagerDelegate> {
    CLLocationManager *LocationManager;
    AVAudioPlayer *audioPlayer;

}

@property (nonatomic, retain) NSTimer *messageTimer;

- (IBAction)LoginButton:(id)sender;

@end

I have the following code in my .m file:

@interface LandingController ()

@end

@implementation LandingController
@synthesize messageTimer;

- (void)checkForMessages
{

    UIAlertView *alert = [[UIAlertView alloc]
                          initWithTitle:@"BINGO:"
                          message:@"Bingo This Works"
                          delegate:nil
                          cancelButtonTitle:@"Okay"
                          otherButtonTitles:nil];

    [alert show];

}

- (IBAction)LoginButton:(id)sender {

    if ([UserType isEqualToString:@"Owner"]) {

        if (messageTimer){ 
        [self.messageTimer invalidate];
        self.messageTimer = nil;
        }

    } else {

        if (!messageTimer){

           self.messageTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
                                                             target:self
                                    selector:@selector(checkForMessages)
                                                           userInfo:nil
                                                            repeats:YES];


        }
    }

}

@end

But my timer doesn't want to stop when I call the invalidate.

The LoginButton is only pressed twice, once when the strResult is = to "Guard" and then the application changes it to be equal to "Owner" and the user presses the login button again, so I don't think I'm setting multiple timers.

After pressing the login button and starting the timer I segue to another view and then segue back to press the login button once more which is when I want the timer to stop. Do I need to do anything special to get the messageTimer since I switched views for a moment and then came back?

Any ideas?

Thanks!

NCoder
  • 325
  • 3
  • 10
  • 25
  • Where have you written scheduledTimerWithTimeInterval code ? Check that, it must be getting initialized again even if you invalidating it. – βhargavḯ May 10 '13 at 05:30
  • 1
    I have it written inside an if loop that only happens once in the application so I don't think it's being re-initialized. – NCoder May 10 '13 at 05:32
  • I've added in more code - see above – NCoder May 10 '13 at 13:27
  • Could you check if the value of [self.messageTimer isValid] is NO before you set self.messageTimer to nil? – robbartoszewski May 10 '13 at 13:44
  • Just added more code, something like that? – NCoder May 10 '13 at 13:49
  • After adding in that code it now doesn't hit the invalidate code. If after setting the timer by pressing the button I seque to another view and then segue back to this view and press the button again, do I need to get the messageTimer value a different way? – NCoder May 10 '13 at 13:53
  • Could you add this code to your viewController: -(void)dealloc {[self.messageTimer invalidate]; self.messageTimer = nil;} and see if it helps? – robbartoszewski May 10 '13 at 14:15
  • It invalidates the timer whenever your viewController is deallocated. I just want to make sure it's not a timer leak. – robbartoszewski May 10 '13 at 14:18
  • I added it but it didn't seem to change anything. – NCoder May 10 '13 at 14:18

7 Answers7

36

You need to call [self.messageTimer invalidate] on the same thread on which you created the timer. Just make sure that the timer is created and invalidated on main thread.

dispatch_async(dispatch_get_main_queue(), ^{
    if ([UserType isEqualToString:@"Owner"]) {
        [self.messageTimer invalidate];
        self.messageTimer = nil;
    } else {
        self.messageTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
                                                             target:self
                                                           selector:@selector(checkForMessages)
                                                           userInfo:nil
                                                            repeats:YES];
    }
});
Borys Verebskyi
  • 4,160
  • 6
  • 28
  • 42
robbartoszewski
  • 896
  • 6
  • 11
  • If I'm calling the one inside the ELSE and the other inside the IF would they not be on the same main thread? – NCoder May 10 '13 at 13:00
  • This IF/ELSE case is brought on when the user presses a button and kicks off (IBAction)LoginButton:(id)sender { //if else statement here } – NCoder May 10 '13 at 13:01
  • 1
    creating and invalidating my audio record scheduled timer in the main thread does not change the fact that the timer never ends. This issue is getting me crazy. – Vilmir Jun 17 '19 at 16:45
20

NSTimer is retained by NSRunLoop, so the only way I see your issue happening is if you're actually creating more than one timer and invalidating only what you have reference to.

Example:

if(!timer)
        timer = [NSTimer scheduledTimerWithTimeInterval:1 target:(self) selector:@selector(processTimer) userInfo:nil repeats:YES];
swiftBoy
  • 35,607
  • 26
  • 136
  • 135
Dmitry Shevchenko
  • 31,814
  • 10
  • 56
  • 62
  • I have it written inside an if loop that only happens once in the application so I don't think it's being re-initialized. Any other ideas?! – NCoder May 10 '13 at 05:33
  • I've updated the code with the IF loop. The first status when the page loads is not OWNER and then it's switched to OWNER which should trigger the stop timer. I know it's hitting the if statements properly as I put alerts in both sections to verify. The only thing is the timer doesn't stop. – NCoder May 10 '13 at 05:40
  • 1
    You're right, I was calling one method twice, and that was Initialising NSTimer. (i.e. In `ViewDidLoad` and `ViewWillAppear`). Thanks! – Mohammad Zaid Pathan Oct 27 '15 at 10:41
  • @DmitryShevchenko Thanks a lot. Now I am gonna follow this as a standard practice. – Rohit Pradhan Jan 25 '18 at 11:17
3

Have you try to put repeat as No

self.messageTimer = [NSTimer scheduledTimerWithTimeInterval:10.0
                                                             target:self
                                                          selector:@selector(checkForMessages)
                                                           userInfo:nil
                                                            repeats:NO];
wesley
  • 859
  • 5
  • 15
  • But if I want it to run the timer every 10 seconds don't I need the repeat set to YES? – NCoder May 10 '13 at 05:42
  • It looks like if I set it to NO it only runs the timer once, but I definitely need it to run continuously until I tell it to stop. – NCoder May 10 '13 at 05:45
  • ok then from where you called this if ([UserType isEqualToString:@"Owner"]){}? Is this in same class instance or any other? – wesley May 10 '13 at 05:50
  • Both in the same instance .m file. – NCoder May 10 '13 at 05:51
  • so some where you have to update the userType before try to stop this timer right? If so how did you update the userType? – wesley May 10 '13 at 05:55
  • It's a web-service call that I make and it returns back from "Guard" to "Owner" which I'm certain is working properly. So the first state is guard, and it happens once, then the second state is Owner and it too happens once. – NCoder May 10 '13 at 05:56
  • So the timer should run from the time the app starts and Guard is the value until the webservice changes it to Owner. – NCoder May 10 '13 at 05:57
  • Does the code definitely get to the line, [self.messageTimer invalidate];? You can check this by using a breakpoint or an NSLog? – wesley May 10 '13 at 06:19
  • Yeah I placed a break point and it's definitely getting to that line. I even put an alert right after it and it's alerting without any issue. – NCoder May 10 '13 at 12:38
2

If the code at the end (starting with if) is called twice with UserType != Owner, you create a new timer without invalidating the old one. Now two timers are running. If the code executes a third time, you will add a third timer and so on. Executing the code with UserType == Owner only invalidates the last timer and even it is called repeatly, it does not invalidate older timers.

You have a timer leak. ;-)

Amin Negm-Awad
  • 16,582
  • 3
  • 35
  • 50
  • I wish it was a leak! I've added in both logs and alerts to verify that only one timer is running and I'm certain that only one is running. The timer start alert only happens once and the timer stop alert only happens once but the timer continues to run. Any other ideas? – NCoder May 10 '13 at 12:54
  • I do not understand, what you want to say with your comment. However, a timer continues running, even the instance of `NSTimer` is deallocated. You have to send `-invalidate`. – Amin Negm-Awad Nov 10 '16 at 14:24
1

How about put an NSLog in your checkForMessages method? It would be easier to check if there's really only 1 timer.

(I'd rather put this in a comment, but I don't have that much reputation....)

Ooops
  • 269
  • 2
  • 12
  • I've added in both logs and alerts to verify that only one timer is running and I'm certain that only one is running. The timer start alert only happens once and the timer stop alert only happens once but the timer continues to run. – NCoder May 10 '13 at 12:54
1

I have an approach for stopping or deactivate the timer, Before apply this make sure that you tried all the cases as mentioned above so you can also understand that why this approach used at last.

 // Instead of self use NSTimer class, it will not provide you any crash and in selector placed any empty function and setRepeats to NO

self.messageTimer = [NSTimer scheduledTimerWithTimeInterval:100.0
                       target:NSTimer selector:@selector(emptyFunctionCalling)
                                                                   userInfo:nil
                                                                    repeats:NO];
[self.messageTimer invalidate];
 self.messageTimer = nil;

So whenever the case occured that timer will not stopping then entering in this code the timer will stops permanently.

Gourav Joshi
  • 2,419
  • 2
  • 27
  • 45
0

Its weird but invalidating passed timer reference and created timer reference worked for me.

    delayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(updateDelayLable:) userInfo:nil repeats:YES];

    -(void)updateSendDataDelayLable:(NSTimer*)timer{
delayValueForGNSS--;

                if (delayValueForGNSSSend==0) {
                     [timer invalidate];
                     timer = nil;
                     [delayTimer invalidate];
                     delayTimer = nil;
                }
            }
Satish Mavani
  • 4,897
  • 2
  • 20
  • 30