7

我想实现一个基于MVC模型的秒表。秒表的观察者模式

秒表使用NSTimer,并在每次超时时调用选择器-(void) tick

我试图让秒表作为可重用性的模型,但我遇到了一些关于如何更新每个刻度的视图控制器的设计问题。

首先我使用tick方法创建了一个协议,并将视图控制器作为其委托。然后视图控制器根据每个滴答的计时器属性更新视图。 elapsedTime是只读NSTimeInterval。

它的工作原理,但我认为这可能是不好的设计。我是Objective-C/Cocoa Touch初学者。我应该使用类似KVO的东西吗?或者有没有更适合模型的解决方案,以通知视图控制器elapsedTime已更改?

+1

第一个问题很好!欢迎来到SO! –

+0

定时器和视图控制器之间的关系究竟是什么?计时器是否由VC拥有? –

+0

谢谢:) 计时器是由VC所有,是的。我已经实现了一个从Timer继承的IntervalTimer,然后VC拥有IntervalTimer,IntervalTimer实际上是给我一些麻烦的一个。 – Jach0

回答

0

最近,我一直在使用块而不是普通的旧@selector's。它创建更好的代码并将逻辑保持在同一位置。

有没有原生块NSTimer支持,但我用从https://gist.github.com/250662/d4f99aa9bde841107622c5a239e0fc6fa37cb179

类别没有回报选择,你保持代码在一个地方:

__block int seconds = 0; 
    NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1 
               repeats:YES 
               usingBlock:^(NSTimer *timer) { 

               seconds++; 
               // Update UI 

               if (seconds>=60*60*2) { 
                [timer invalidate]; 
               } 




}]; 
5

定时器是一个好办法确保你定期更新你的用户界面,但不要用它来记录时间。 NSTimer can drift,如果您使用计时器来累计秒数,任何小错误都可能累积。

相反,使用NSTimer触发更新您的UI的方法,但使用NSDate获得实时。 NSDate会给你毫秒分辨率;如果你真的需要比这更好的话,请考虑this suggestion to use Mach's timing functions。因此,使用NSDate的,你的代码可能是这样的:

- (IBAction)startStopwatch:(id)sender 
{ 
    self.startTime = [NSDate date]; 
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 
                target:self 
               selector:@selector(tick:) 
               userInfo:repeats:YES]; 
} 

- (void)tick:(NSTimer*)theTimer 
{ 
    self.elapsedTime = [self.startTime timeIntervalSinceNow]; 
    [self updateDisplay]; 
} 

- (IBAction)stopStopwatch:(id)sender 
{ 
    [self.timer invalidate]; 
    self.timer = nil; 
    self.elapsedTime = [self.startTime timeIntervalSinceNow]; 
    [self updateDisplay]; 
} 

如果允许重新启动,等你的代码可能会有点更复杂,但这里最重要的是,你不使用的NSTimer来测量总经过时间。

您可以在this SO thread中找到其他有用的信息。

2

我会建议针对这个问题的KVO。它在这里引入了很多复杂性(和几个令人讨厌的陷阱),对此没有多大的益处。 KVO在需要确保最小开销的情况下非常重要。 Apple在低层次,高性能对象(如图层)中使用它很多。当没有观察者时,它是唯一通用的解决方案,可以提供零开销。大多数情况下,你不需要这些。正确处理KVO可能会很棘手,而且它可以创建的错误令人讨厌追踪。

您的委托方法没有任何问题。这是正确的MVC。你唯一需要担心的是NSTimer没有对它被叫的时间作出强有力的承诺。在某些情况下,重复计时器甚至可以跳过。为了避免这个问题,您通常需要根据当前时间计算elapsedTime,而不是增加它。如果计时器可以暂停,那么你需要保留一个累加器和一个“我上次开始的时间”的日期。

如果需要更高的精度和成本较低的定时器,你可以看看dispatch_source_set_timer(),但对于一个简单的人,有针对性的秒表,NSTimer是罚款,并作一个简单的项目的最佳选择。

+0

我知道跳过计时器和使用增量的问题。我使用NSDate和setElapsedTime我比较NSDates(+使用暂停时的偏移量) – Jach0

+0

我试图实现一个间隔倒数计时器,该计时器从秒表继承并具有一些额外的属性,包括workInterval和restInterval,但标签在我的ViewController get的更新有点奇怪。我认为这可能与ViewController的tick,Timers tick和IntervalTimers tick之间的顺序调用有关。 – Jach0