NSTimer objects are not exact. They depend on the app visiting the event loop frequently, and can vary by 50 MS or more (according to what I've read in the docs). If I remember correctly they try to "snap back" to the desired time interval rather than drifting, but any given firing will not be exact.
That said, I guess what I would do is to take the current NSDate, convert it to an NSTimeInterval, take the ceiling value (the next higher whole number) and start a one-time timer that will fire at that moment. Then in the handler for that timer, start a once-a-second timer. Something like this:
//Get the current date in seconds since there reference date.
NSTimeInterval nowInterval =[NSDate timeInervalSinceReferenceDate];
//Figure out the next even second time interval.
NSTimeInterval nextWholeSecond = ceil(nowInterval);
//Figure out the fractional time between now and the next even second
NSTimeInterval fractionUntilNextSecond = nextWholeSecond - nowInterval;
//Start a one-time timer that will go off at the next whole second.
NSTimer oneTimeTimer = [NSTimer timerWithTimeInterval: fractionUntilNextSecond
target: self
@selector: (startSecondTimer:)
userInfo: nil
repeats: NO];
And the startSecondTimer method:
- (void) startSecondTimer: (NSTimer *)timer;
{
//Start a new, repeating timer that fires once per second, on the second.
self.secondsTimer = [NSTimer timerWithTimeInterval: 1.0
target: self
@selector: (handleSecondTimer:)
userInfo: nil
repeats: YES];
}
You should still calculate the new time in each call to your handleSecondTimer: method rather than relying on the number of times you are called, because if the system gets really busy at the moment when it's supposed to call your timer and can't get to you, it might skip a call completely.
Disclaimer: I haven't tried this, but it should work. My only concern is edge cases. For example, when the next whole second is too close to now and the one-time timer can't fire fast enough. It might be safer to add a second to the fractionUntilNextSecond value, so the second hand doesn't start running for greeter than 1 second but less than 2 seconds.