すみません、パーティーに遅れました。メソッドがタイマーに基づいてコールバックされ、メソッドを相互に同時に実行したいが、メソッド自体を同期させたい場合は、GCDタイマーを使用することをお勧めします。
基本的に、2つのタイマーがあります。1つはmethodOneを実行し、もう1つはmethodTwoを実行します。ブロックをGCDタイマーに渡すので、メソッドを使用する必要はありません。特に、他のコードが実行されるはずのないときにそれらのメソッドを呼び出さないようにする場合はなおさらです。
タイマーを並行キューにスケジュールする場合、両方のタイマーが異なるスレッドで同時に実行されている可能性があります。ただし、タイマー自体はスケジュールされたときにのみ実行されます。これが私がハックしたばかりの例です...シングルトンで簡単に使用できます...
まず、タイマーが起動したときに呼び出されるブロックを取得するタイマーを作成するヘルパー関数。ブロックはオブジェクトを渡すため、保持サイクルを作成せずにブロックから参照できます。パラメータ名としてselfを使用すると、ブロック内のコードは他のコードと同じようになります...
static dispatch_source_t setupTimer(Foo *fooIn, NSTimeInterval timeout, void (^block)(Foo * self)) {
// Create a timer that uses the default concurrent queue.
// Thus, we can create multiple timers that can run concurrently.
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
uint64_t timeoutNanoSeconds = timeout * NSEC_PER_SEC;
dispatch_source_set_timer(timer,
dispatch_time(DISPATCH_TIME_NOW, timeoutNanoSeconds),
timeoutNanoSeconds,
0);
// Prevent reference cycle
__weak Foo *weakFoo = fooIn;
dispatch_source_set_event_handler(timer, ^{
// It is possible that the timer is running in another thread while Foo is being
// destroyed, so make sure it is still there.
Foo *strongFoo = weakFoo;
if (strongFoo) block(strongFoo);
});
return timer;
}
さて、基本的なクラスの実装。methodOneとmethodTwoを公開したくない場合、特にそれらが単純な場合は、そのコードをブロックに直接配置できるため、それらを作成する理由はありません。
@implementation Foo {
dispatch_source_t timer1_;
dispatch_source_t timer2_;
}
- (void)methodOne {
NSLog(@"methodOne");
}
- (void)methodTwo {
NSLog(@"methodTwo");
}
- (id)initWithTimeout1:(NSTimeInterval)timeout1 timeout2:(NSTimeInterval)timeout2 {
if (self = [super init]) {
timer1_ = setupTimer(self, timeout1, ^(Foo *self) {
// Do "methodOne" work in this block... or call it.
[self methodOne];
});
timer2_ = setupTimer(self, timeout2, ^(Foo *self) {
// Do "methodOne" work in this block... or call it.
[self methodTwo];
});
dispatch_resume(timer1_);
dispatch_resume(timer2_);
}
return self;
}
- (void)dealloc {
dispatch_source_cancel(timer2_);
dispatch_release(timer2_);
dispatch_source_cancel(timer1_);
dispatch_release(timer1_);
}
@end
コメントに応じて編集
します(ブロックが同時に実行されない理由、および欠落したタイマーが1つに統合される理由を説明するための詳細を含む)。
複数回実行されているかどうかを確認する必要はありません。ドキュメントから直接...
ディスパッチソースは再入可能ではありません。ディスパッチソースが一時停止されている間、またはイベントハンドラブロックが現在実行されている間に受信されたイベントは、ディスパッチソースが再開された後、またはイベントハンドラブロックが返された後に合体して配信されます。
つまり、GCD dispatch_sourceタイマーブロックがディスパッチされると、すでに実行されているものが完了するまで、それは再度ディスパッチされません。何もしません。ライブラリ自体が、ブロックが同時に複数回実行されないようにします。
そのブロックにタイマー間隔よりも長い時間がかかる場合、「次の」タイマー呼び出しは、実行中のタイマー呼び出しが完了するまで待機します。また、配信されたはずのすべてのイベントが1つのイベントにまとめられます。
あなたは呼び出すことができます
unsigned numEventsFired = dispatch_source_get_data(timer);
ハンドラー内から、ハンドラーが最後に実行されてから発生したイベントの数を取得します(たとえば、ハンドラーが4回のタイマー起動を実行した場合、これは4回になりますが、この1回のイベントですべての発生が発生します-それらの個別のイベントを受け取ることはありません)。
たとえば、インターバルタイマーが1秒で、タイマーの実行に5秒かかるとします。そのタイマーは、現在のブロックが完了するまで再び起動しません。さらに、これらのタイマーはすべて1つに統合されるため、5つではなく、1つのブロックに1つの呼び出しがあります。
さて、そうは言っても、バグだと思うことについて注意する必要があります。今、私はライブラリコードの足元にバグを置くことはめったにありませんが、これは繰り返し可能であり、ドキュメントに反しているようです。したがって、バグでない場合は、文書化されていない機能です。ただし、回避するのは簡単です。
タイマーを使用するとき、合体したタイマーが最も確実に合体することに気づきました。つまり、タイマーハンドラーが実行されていて、実行中に5つのタイマーが起動された場合、ブロックはすぐに呼び出され、見逃された5つのイベントを表します。ただし、それが完了するとすぐに、以前に見逃されたタイマーイベントの数に関係なく、ブロックは1回だけ再度実行されます。
ただし、dispatch_source_get_data(timer)は0を返すため、これらを簡単に識別できます。これは、ブロックが最後に呼び出されてからタイマーイベントが発生していないことを意味します。
したがって、私はこのコードをタイマーハンドラーの最初の行として追加することに慣れてきました...
if (dispatch_source_get_data(timer) == 0) return;