我正在尝试基于MVC模型实现秒表。
秒表使用NSTimer,每次超时都会调用选择器-(void) tick
。
我试图将秒表作为可重用性的模型,但是我遇到了一些关于如何为每个滴答更新视图控制器的设计问题。
首先,我使用tick方法创建了一个协议,并使视图控制器成为委托。然后,视图控制器根据每个刻度线上的计时器属性更新视图。 elapsedTime是一个只读的NSTimeInterval。
它有效,但我认为这可能是糟糕的设计。我是Objective-C / Cocoa Touch初学者。我应该使用像KVO这样的东西吗?或者是否有更优雅的解决方案让模型通知视图控制器elapsedTime
已更改?
答案 0 :(得分: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中找到其他有用的信息。
答案 1 :(得分:2)
对于这个问题,我建议反对KVO。它引入了很多复杂性(以及几个恼人的陷阱),这里没什么好处。在需要确保绝对最小开销的情况下,KVO非常重要。对于像图层这样的低级高性能对象,Apple会大量使用它。它是唯一通用的解决方案,在没有观察者时提供零开销。大多数时候,你不需要那样做。正确处理KVO可能会非常棘手,而且它可以创建的错误很容易被追踪。
您的委托方法没有任何问题。这是正确的MVC。你真正需要担心的唯一一件事就是NSTimer没有对它何时被召唤做出强有力的承诺。在某些情况下甚至允许重复计时器跳过。为了避免这个问题,您通常希望根据当前时间而不是通过递增来计算elapsedTime
。如果计时器可以暂停,那么你需要保留一个累加器和“我上次开始的时间”日期。
如果您需要更高精度或更低成本的计时器,您可以查看dispatch_source_set_timer()
,但对于一个简单的以人为目标的秒表,NSTimer
很好,是一个简单项目的绝佳选择
答案 2 :(得分:0)
最近,我一直在使用块而不是普通的旧@selector
。它创建更好的代码并将逻辑保持在同一位置。
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];
}
}];