質問

I have a method which displays a clock with seconds and the current time. This works fine except that this code will get called either half way through the current second or three quarters of the way through the current second depending on what time I open the app or run it. The method is called through the viewDidLoad method. When this happens my clock will be off up to almost 1 second. Is there any way to start my method when the next second start exactly? i.e. start it when the devices time is HH:MM:SS.000? Note: sorry if this is confusing with the excessive use of second and clock. I just mean I need to start my method at HH:MM:SS.000 (devices internal clock)

役に立ちましたか?

解決

Using:

- (id)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)seconds 
    target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo 
    repeats:(BOOL)repeats

With an object of NSTimer is probably the way to go.

Add the logic found in this StackOverflow question/answers and you should be able to get it right on an exact second. (Use the logic there to create an NSDate object with resolution to 1 second, then use that date in the method I mentioned above).

NSTimer *yourTimer = [[NSTimer alloc] initWithFireDate:nowToTheSecond 
    interval:1.0 target:self selector:@selector(updateClock) userInfo:nil
    repeats:YES];
[[NSRunLoop mainLoop] addTimer:yourTimer forMode:NSRunLoopCommonModes];

他のヒント

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top