7

私はMVCモデルに基づいてストップウォッチを実装しようとしています。ストップウォッチのオブザーバーパターン

ストップウォッチはNSTimerを使用し、セレクタ-(void) tickはタイムアウトごとに呼び出されます。

私は再利用性のモデルとしてストップウォッチを作成しようとしましたが、各ティックごとにビューコントローラを更新する方法に関するいくつかの設計上の問題にぶつかってきました。

まず、チックメソッドを使用してプロトコルを作成し、ビューコントローラをその代理人にしました。次に、ビューコントローラーは、各ティックでタイマーのプロパティに基づいてビューを更新します。 elapsedTimeは読み取り専用のNSTimeIntervalです。

それは動作しますが、私はそれが悪いデザインかもしれないと思っています。私はObjective-C/Cocoa Touchの初心者です。私はKVOのようなものを使うべきですか?または、モデルがelapsedTimeが変更されたビューコントローラに通知するためのより洗練されたソリューションがありますか?

+1

初めての質問です!ようこそ! –

+0

タイマーとView Controllerの関係はどういうものですか?タイマーはVCによって所有されていますか? –

+0

ありがとうございました:) タイマーはVCによって所有されています。私はTimerから継承したIntervalTimerを実装していますが、代わりにVCがIntervalTimerを所有しています - IntervalTimerは実際に私に少し問題を与えるものです。 – Jach0

答えて

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]; 
               } 




}]; 
5

タイマーがする良い方法ですユーザーインターフェイスを定期的に更新するようにしてください。ただし、ユーザーインターフェイスを使用して時間を追跡する必要はありません。 NSTimer can driftであり、タイマーを使用して秒を累積すると小さなエラーが蓄積することがあります。

代わりに、NSTimerを使用してUIを更新するメソッドをトリガーしますが、NSDateを使用してリアルタイムを取得します。 NSDateはミリ秒の分解能を与えます。それよりも本当に必要な場合は、this suggestion to use Mach's timing functionsと考えてください。あなたがなど、再起動を許可しますが、ここで重要なことは、あなたがNSTimerを使用していないということであればあなたのコードは、もう少し洗練されたかもしれません

- (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]; 
} 

:だから、NSDateを使用して、あなたのコードは次のようなものかもしれません総経過時間を測定する。

さらに便利な情報は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のティック、Timersティック、そしてIntervalTimersティックの間のシーケンシャルな呼び出しに何か関係があるかもしれないと思った。 – Jach0

関連する問題