In a certain view I wish to have a certain method fired when the firing timing is based on an array of NSTimeIntervals (which I keep in as NSArray holding double values).
So I have a method that does the relevant action according to the relevant timing, and within this method I call the same method (recursively) to be fired again in the next scheduled time:
-(void) actionMethod:(int)someDataINeed {
//do something and then...
if (someDataINeed == someDefinitionIHaveToBreakTheLoop) {
return;
}
double nextFire = [self aMethodThatCalculatesTheNextFiringTime];
NSNumber * nextDataINeed = [NSNumber numberWithInt:someDataINeed+1];
NSTimer * aTimer = [NSTimer scheduledTimerWithTimeInterval:nextFire
target:self
selector:@selector(actionMethod:)
userInfo:nextDataINeed
repeats:NO];
}
Well... it doesn't work (if it did, I guess I wouldn't ask about it...). When I NSLog it, it seems like the time is not running at all and that the timer is called in some sort of loop and not "being fired" according to the timing I had defined. The nextFire double data is correct (according to my definitions).
If I have it wrong, I would appreciate if someone can direct me to how this type of action should be performed.
If I got it right, but simply write it wrong, an eye that catches my "bug" would be appreciated as well...
When you schedule an NSTimer instance, the run loop retains the timer, so there's no need to worry about retaining it yourself (ARC or not).
The timer also retains its target, so you don't have to worry about a situation where the target is deallocated first. However, if the target is a view, it might receive a timer message after it has been removed from the view hierarchy. In that case you will want to ignore the message.
You can handle this two ways:
In your actionMethod
, be sure to check that you still want to perform the action. If not, ignore the message and let everything be cleaned up.
Retain a reference to the timer instance, and call [timer invalidate]
when you no longer want to receive timer messages.
If you can be sure that the target will always be around and it will always be OK to receive a message, you don't need to do anything. For example, your app delegate object will be around for the lifetime of your application.
You cannot set a timer to call just any method on any object. The timer method MUST take only one parameter, which is the NSTimer instance that sent the message. You have defined actionMethod:
to take an int
, which will actually compile and execute, but when the timer fires and the method is called, the value of someDataINeed
will be the memory address of the NSTimer object. This isn't very useful.
You need to define your method this way:
- (void)actionMethod:(NSTimer *)timer
To get the someDataINeed
value, you can save it as an NSNumber in the timer's userInfo. It is there just so you can add some data to the timer.
NSNumber *number = [timer userInfo];
int someDataINeed = [number intValue];
When you create the timer, you can wrap the nextDataINeed
into an NSNumber and set the userInfo this way:
currentTimer = [NSTimer scheduledTimerWithTimeInterval:nextFire
target:self
selector:@selector(actionMethod:)
userInfo:[NSNumber numberWithInt:nextDataINeed]
repeats:NO];
You need to use NSNumber because the userInfo must be an object.
If applying the above doesn't fix the problem, one thing to check is that aMethodThatCalculatesTheNextFiringTime
is returning the time interval since now, and not since a reference date. If you want a timer to fire in three seconds, the value of nextFire
must be 3.0
. If you accidentally call timeIntervalSinceReferenceDate
you'll get a huge number, and the timer will be scheduled to fire several years into the future.