メインキューにディスパッチして繰り返しNSTimer
を作成する理由を誰かが説明できるかどうか疑問に思っています。それを実行するために、それをRUN LOOPに追加する必要がありますか? performselectorOnMainThread
を使用している場合でも、起動するにはRUNLOOPに追加する必要があります。
以下は私の質問の例です:
#define queue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
#define mainqueue dispatch_get_main_queue()
- (void)someMethodBeginCalled
{
dispatch_async(queue, ^{
int x = 0;
dispatch_async(mainqueue, ^(void){
if([_delegate respondsToSelector:@selector(complete:)])
[_delegate complete:nil];
});
});
}
- (void)compelete:(id)object
{
[self startTimer];
//[self performSelectorOnMainThread:@selector(startTimer) withObject:nil waitUntilDone:NO];
}
- (void)startTimer
{
NSTimer timer = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(callsomethingelse) userInfo:nil repeats:YES];
//NSDefaultRunLoopMode
[[NSRunLoop currentRunLoop] addTimer:_busTimer forMode:NSRunLoopCommonModes];
}
編集:私はこの質問の言い回しが非常に貧弱だと思います。知りたいのですがwhystartTimer
を呼び出す場合、someMethodBeginCalled
に[[NSRunLoop currentRunLoop] addTimer:_busTimer forMode:NSRunLoopCommonModes];
が必要です。その行を含めないと、タイマーは起動しません。
たとえば、startTimer
からviewDidLoad
を呼び出すと、NSRunLoop
行を削除でき、タイマーは60秒ごとに起動します。
代わりに、いつでもこの方法を使用できます。
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(getBusLocation) userInfo:nil repeats:YES];
これにより、実行ループに自動的に追加されるため、行が節約されます。
そして、ランループにNSTimer
を追加する方法は次のとおりです。
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
なぜなら、 docs のように:
タイマーは実行ループと連動して機能します。タイマーを効果的に使用するには、実行ループがどのように動作するかを知っておく必要があります。「NSRunLoopおよびスレッドプログラミングガイド」を参照してください。特に、実行ループはタイマーを保持するため、実行ループにタイマーを追加した後でタイマーを解放できることに注意してください。
AppleがNSTimerのコードを書いたときに行われたのは設計上の決定であり(そして、そうする正当な理由があると確信しています)、それを回避するために私たちにできることは何もありません。本当にそんなに面倒なの?
@sosbornが言ったように、NSTimer
sはNSRunLoop
sに依存し、GCDキューは実行ループのないスレッドを作成するため、NSTimer
はGCD
。
この問題に関する他のStackOverflowの質問を確認してください: GCDシリアルキューでNSTimersをスケジュールして無効にするのは安全ですか?
その問題を解決するために、私はMSWeakTimer
を実装しました: https://github.com/mindsnacks/MSWeakTimer (そして最後のWWDCでlibdispatchエンジニアに実装をチェックしてもらいました!)
私の場合、runloopにタイマーを追加しても機能しませんでした。メインスレッドでタイマーを作成する必要がありました。私はMultipeerConnectivityデリゲートでこのスレッドの作成を行っていました。
dispatch_async(dispatch_get_main_queue(), ^{
self.timer = [NSTimer scheduledTimerWithTimeInterval:self.interval invocation: self.invocation repeats:YES];
});
GCDキューは実行ループのないスレッドを作成するため、タイマーメソッドは呼び出されません
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"Timer method from GCD main queue");
}];
});
ただし、メインキューにディスパッチされると、タイマーメソッドが呼び出され、メインスレッドの実行ループに追加されます。
dispatch_async(dispatch_get_main_queue(), ^{
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"Timer method from GCD main queue");
}];
});