web-dev-qa-db-ja.com

NSTimerでrunloopに追加する必要があります

メインキューにディスパッチして繰り返し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秒ごとに起動します。

19
chicken

代わりに、いつでもこの方法を使用できます。

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(getBusLocation) userInfo:nil repeats:YES];

これにより、実行ループに自動的に追加されるため、行が節約されます。

14
borrrden

そして、ランループにNSTimerを追加する方法は次のとおりです。

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
20

なぜなら、 docs のように:

タイマーは実行ループと連動して機能します。タイマーを効果的に使用するには、実行ループがどのように動作するかを知っておく必要があります。「NSRunLoopおよびスレッドプログラミングガイド」を参照してください。特に、実行ループはタイマーを保持するため、実行ループにタイマーを追加した後でタイマーを解放できることに注意してください。

AppleがNSTimerのコードを書いたときに行われたのは設計上の決定であり(そして、そうする正当な理由があると確信しています)、それを回避するために私たちにできることは何もありません。本当にそんなに面倒なの?

4
sosborn

@sosbornが言ったように、NSTimersはNSRunLoopsに依存し、GCDキューは実行ループのないスレッドを作成するため、NSTimerGCD

この問題に関する他のStackOverflowの質問を確認してください: GCDシリアルキューでNSTimersをスケジュールして無効にするのは安全ですか?

その問題を解決するために、私はMSWeakTimerを実装しました: https://github.com/mindsnacks/MSWeakTimer (そして最後のWWDCでlibdispatchエンジニアに実装をチェックしてもらいました!)

3
Javier Soto

私の場合、runloopにタイマーを追加しても機能しませんでした。メインスレッドでタイマーを作成する必要がありました。私はMultipeerConnectivityデリゲートでこのスレッドの作成を行っていました。

    dispatch_async(dispatch_get_main_queue(), ^{
        self.timer = [NSTimer scheduledTimerWithTimeInterval:self.interval  invocation: self.invocation repeats:YES];
    });
0
Chris Prince

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

});
0
Say2Manuj