9

I'm really frustrated now, googled the whole internet, stumbled through SO and still didn't find a solution.

I'm trying to implement an NSTimer, but the method which I defined doesn't get called. (seconds are set correctly, checked it with breakpoints). Here is the code:

- (void) setTimerForAlarm:(Alarm *)alarm {
    NSTimeInterval seconds = [[alarm alarmDate] timeIntervalSinceNow];
    theTimer = [NSTimer timerWithTimeInterval:seconds 
                            target:self 
                          selector:@selector(showAlarm:)
                          userInfo:alarm repeats:NO];
}

- (void) showAlarm:(Alarm *)alarm {
    NSLog(@"Alarm: %@", [alarm alarmText]);
}

The object "theTimer" is deined with @property:

@interface FooAppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate>  {
@private

    NSTimer *theTimer;

}

@property (nonatomic, retain) NSTimer *theTimer;

- (void) setTimerForAlarm:(Alarm *)alarm;
- (void) showAlarm:(Alarm *)alarm;

What am I doing wrong?

tamasgal
  • 24,826
  • 18
  • 96
  • 135
  • 2
    The method signature for `showAlarm:(Alarm *)alarm` should be `showAlarm:(NSTimer *)timer` instead. Then you would get the Alarm object with `Alarm *alarm = [timer userInfo]`. – 0xced May 20 '11 at 18:49

5 Answers5

27

timerWithTimeInterval simply creates a timer, but doesn't add it to any run loops for execution. Try

self.theTimer = [NSTimer scheduledTimerWithTimeInterval:seconds 
                        target:self 
                      selector:@selector(showAlarm:)
                      userInfo:alarm repeats:NO];

instead.

JimDusseau
  • 729
  • 6
  • 6
8

Also don't forget to check if

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds 
                                     target:(id)target 
                                   selector:(SEL)aSelector 
                                   userInfo:(id)userInfo 
                                    repeats:(BOOL)repeats 

is called in the main thread.

Petr Syrov
  • 14,689
  • 3
  • 20
  • 30
7

You've created an NSTimer object but you haven't scheduled it to be run. timerWithTimeInterval:target:selector:userInfo:repeats: creates a timer that you can schedule to run later, for example, to create a timer at application launch and have it start counting when the user presses a button. Either call

[[NSRunLoop currentRunLoop] addTimer:theTimer forMode:NSDefaultRunLoopMode]

at the end of setTimerForAlarm or replace

theTimer = [NSTimer timerWithTimeInterval:seconds 
                            target:self 
                          selector:@selector(showAlarm:)
                          userInfo:alarm repeats:NO];

with

theTimer = [NSTimer scheduledTimerWithTimeInterval:seconds 
                            target:self 
                          selector:@selector(showAlarm:)
                          userInfo:alarm repeats:NO];

which creates a timer and immediately schedules it.

Christina
  • 795
  • 7
  • 9
  • +1 for being both correct and thorough! (Actually, I have to wait until midnight to upvote -- I've used my day's suppy already.) – jscs May 20 '11 at 20:25
2

Well you may want to actually schedule your NSTimer on the run loop :) instead of timerWithTimeInterval use scheduledTimerWithTimeInterval.

theTimer = [NSTimer scheduledTimerWithTimeInterval:seconds 
                        target:self 
                      selector:@selector(showAlarm:)
                      userInfo:alarm repeats:NO];
Joe
  • 56,979
  • 9
  • 128
  • 135
2

While all of the answers are right, there is an even simpler solution that doesn't involve a NSTimer at all. Your setTimerForAlarm: implementation can be reduced to one simple line:

[self performSelector:@selector(showAlarm:) withObject:alarm afterDelay:[[alarm alarmDate] timeIntervalSinceNow]]
0xced
  • 25,219
  • 10
  • 103
  • 255
  • Thanks, but NSTimer is the better choice at this point, because I also want to cancel the Timer anytime. :-) – tamasgal May 20 '11 at 22:03
  • You can also cancel a `performSelector:withObject:afterDelay:` with `+[NSObject cancelPreviousPerformRequestsWithTarget:selector:object:]` – 0xced May 22 '11 at 14:52
  • 1
    Thank you for this post! It's a massive time saver and my code is more compact. – Adrian Aug 09 '15 at 12:27