How do I use NSTimer?
Asked Answered
G

6

341

How do I use an NSTimer? Can anyone give me step by step instructions?

Gunshy answered 19/9, 2009 at 17:25 Comment(1)
i got parameterwise info on pravinmagdum.wordpress.com/2010/12/30/how-to-use-nstimer have look on it .. may be helpful for youCrosshatch
D
612

Firstly I'd like to draw your attention to the Cocoa/CF documentation (which is always a great first port of call). The Apple docs have a section at the top of each reference article called "Companion Guides", which lists guides for the topic being documented (if any exist). For example, with NSTimer, the documentation lists two companion guides:

For your situation, the Timer Programming Topics article is likely to be the most useful, whilst threading topics are related but not the most directly related to the class being documented. If you take a look at the Timer Programming Topics article, it's divided into two parts:

  • Timers
  • Using Timers

For articles that take this format, there is often an overview of the class and what it's used for, and then some sample code on how to use it, in this case in the "Using Timers" section. There are sections on "Creating and Scheduling a Timer", "Stopping a Timer" and "Memory Management". From the article, creating a scheduled, non-repeating timer can be done something like this:

[NSTimer scheduledTimerWithTimeInterval:2.0
    target:self
    selector:@selector(targetMethod:)
    userInfo:nil
    repeats:NO];

This will create a timer that is fired after 2.0 seconds and calls targetMethod: on self with one argument, which is a pointer to the NSTimer instance.

If you then want to look in more detail at the method you can refer back to the docs for more information, but there is explanation around the code too.

If you want to stop a timer that is one which repeats, (or stop a non-repeating timer before it fires) then you need to keep a pointer to the NSTimer instance that was created; often this will need to be an instance variable so that you can refer to it in another method. You can then call invalidate on the NSTimer instance:

[myTimer invalidate];
myTimer = nil;

It's also good practice to nil out the instance variable (for example if your method that invalidates the timer is called more than once and the instance variable hasn't been set to nil and the NSTimer instance has been deallocated, it will throw an exception).

Note also the point on Memory Management at the bottom of the article:

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.

Darbie answered 19/9, 2009 at 17:58 Comment(7)
Okk, one question, what would I put in as my code that would be executed every two seconds?Gunshy
You'd pass YES for repeats: when you call scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:. If you do so then make sure to keep a reference to the NSTimer instance (it is returned by the method) and follow the point on Memory Management as I detailed above.Darbie
No, where would I put my code that would be started every 2 seconds? For example, let's say I wanted it to make a beep noise every 2 seconds. where would I put the beep noise code..?Gunshy
In the method that you specify with the target and selector. For example, if your target is self and the selector is timerMethod:, the method called when the timer fires is timerMethod: defined on the self. You can then put whatever code you want in that method, and the method will be called whenever the timer fires. Note that the method called when the timer fires (that you pass in as the selector:) can only take one argument (which when called is a pointer to the NSTimer instance).Darbie
Sorry, meant "defined on self"Darbie
so would this coding be correct... (inputBox is a NSTextField) [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(targetMethod:[inputBox setStringValue@"blah"]) userInfo:nil repeats:NO];Gunshy
No that's a method call, not a selector. And if you want to pass arguments to the timer method you have to use an NSInvocation. Take a look at this question, which might be more what you're looking for https://mcmap.net/q/94394/-arguments-in-selector/…Darbie
S
329

there are a couple of ways of using a timer:

1) scheduled timer & using selector

NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 2.0
                      target: self
                      selector:@selector(onTick:)
                      userInfo: nil repeats:NO];
  • if you set repeats to NO, the timer will wait 2 seconds before running the selector and after that it will stop;
  • if repeat: YES, the timer will start immediatelly and will repeat calling the selector every 2 seconds;
  • to stop the timer you call the timer's -invalidate method: [t invalidate];

As a side note, instead of using a timer that doesn't repeat and calls the selector after a specified interval, you could use a simple statement like this:

[self performSelector:@selector(onTick:) withObject:nil afterDelay:2.0];

this will have the same effect as the sample code above; but if you want to call the selector every nth time, you use the timer with repeats:YES;

2) self-scheduled timer

NSDate *d = [NSDate dateWithTimeIntervalSinceNow: 60.0];
NSTimer *t = [[NSTimer alloc] initWithFireDate: d
                              interval: 1
                              target: self
                              selector:@selector(onTick:)
                              userInfo:nil repeats:YES];

NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer:t forMode: NSDefaultRunLoopMode];
[t release];
  • this will create a timer that will start itself on a custom date specified by you (in this case, after a minute), and repeats itself every one second

3) unscheduled timer & using invocation

NSMethodSignature *sgn = [self methodSignatureForSelector:@selector(onTick:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature: sgn];
[inv setTarget: self];
[inv setSelector:@selector(onTick:)];

NSTimer *t = [NSTimer timerWithTimeInterval: 1.0
                      invocation:inv 
                      repeats:YES];

and after that, you start the timer manually whenever you need like this:

NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer: t forMode: NSDefaultRunLoopMode];



And as a note, onTick: method looks like this:

-(void)onTick:(NSTimer *)timer {
   //do smth
}
Samathasamau answered 19/9, 2009 at 17:58 Comment(5)
Ok, but you see I want to lower the transparency of my app, so I don't know how to apply that with the NSTimerGunshy
jeez, these people today.. vote down from me because you didn't specified that from the start and let us write in vain!Samathasamau
You didn't write in vain. This is GOOD info!Adsorb
In method 2, "self-scheduled timer", how can I stop the timer when I want?Marigolda
@Satyamsvv, you can stop the timer by invoking, say another method having: [timer invalidate]; timer = nil;Cordes
W
59

Something like this:

NSTimer *timer;

    timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
                     target: self
                     selector: @selector(handleTimer:)
                     userInfo: nil
                     repeats: YES];
Worried answered 19/9, 2009 at 17:30 Comment(1)
Still going strong ... 2014 too!Susannahsusanne
N
5
#import "MyViewController.h"

@interface MyViewController ()

@property (strong, nonatomic) NSTimer *timer;

@end

@implementation MyViewController

double timerInterval = 1.0f;

- (NSTimer *) timer {
    if (!_timer) {
        _timer = [NSTimer timerWithTimeInterval:timerInterval target:self selector:@selector(onTick:) userInfo:nil repeats:YES];
    }
    return _timer;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

-(void)onTick:(NSTimer*)timer
{
    NSLog(@"Tick...");
}

@end
Norwich answered 17/12, 2013 at 17:49 Comment(1)
Somehow it creates retain cycle, so the MyViewController never gets deallocated.Mccants
A
5
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(timerCalled) userInfo:nil repeats:NO];

-(void)timerCalled
{
     NSLog(@"Timer Called");
     // Your Code
}
Alvinia answered 24/3, 2014 at 7:20 Comment(5)
where did you write this?Alvinia
@JigneshB this answer is only about to how to use NSTimer not about use it in backgroundAlvinia
i writed in background method i.e -(void)applicationDidEnterBackground:(UIApplication *)application {}Peggy
with repeated mode YESPeggy
Apple's documentation is very specific about the signature of the callback method. Yours is wrong.Komarek
C
2

The answers are missing a specific time of day timer here is on the next hour:

NSCalendarUnit allUnits = NSCalendarUnitYear   | NSCalendarUnitMonth |
                          NSCalendarUnitDay    | NSCalendarUnitHour  |
                          NSCalendarUnitMinute | NSCalendarUnitSecond;

NSCalendar *calendar = [[ NSCalendar alloc]  
                          initWithCalendarIdentifier:NSGregorianCalendar];

NSDateComponents *weekdayComponents = [calendar components: allUnits 
                                                  fromDate: [ NSDate date ] ];

[ weekdayComponents setHour: weekdayComponents.hour + 1 ];
[ weekdayComponents setMinute: 0 ];
[ weekdayComponents setSecond: 0 ];

NSDate *nextTime = [ calendar dateFromComponents: weekdayComponents ];

refreshTimer = [[ NSTimer alloc ] initWithFireDate: nextTime
                                          interval: 0.0
                                            target: self
                                          selector: @selector( doRefresh )
                                          userInfo: nil repeats: NO ];

[[NSRunLoop currentRunLoop] addTimer: refreshTimer forMode: NSDefaultRunLoopMode];

Of course, substitute "doRefresh" with your class's desired method

try to create the calendar object once and make the allUnits a static for efficiency.

adding one to hour component works just fine, no need for a midnight test (link)

Cosignatory answered 25/10, 2013 at 23:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.