0

特定のビューでは、起動タイミングが NSTimeIntervals の配列に基づいている場合に特定のメソッドを起動させたいと考えています (これは、double 値を保持する NSArray として保持しています)。

したがって、関連するタイミングに従って関連するアクションを実行するメソッドがあり、このメソッド内で同じメソッドを (再帰的に) 呼び出して、次のスケジュールされた時間に再度起動します。

-(void) actionMethod:(int)someDataINeed {

    //do something and then...

    if (someDataINeed == someDefinitionIHaveToBreakTheLoop) {
        return;
    }

    double nextFire = [self aMethodThatCalculatesTheNextFiringTime];
    NSNumber * nextDataINeed = [NSNumber numberWithInt:someDataINeed+1];

    NSTimer * aTimer = [NSTimer scheduledTimerWithTimeInterval:nextFire 
                                        target:self 
                                        selector:@selector(actionMethod:)
                                        userInfo:nextDataINeed 
                                        repeats:NO];
}

うーん...うまくいきません(うまくいったとしても、私はそれについて尋ねません...)。NSLog を実行すると、時間がまったく実行されていないように見え、タイマーがある種のループで呼び出され、定義したタイミングに従って「起動」されていないようです。nextFire double データは正しいです (私の定義によると)。

間違っている場合は、この種のアクションを実行する方法を教えていただければ幸いです。

私がそれを正しく書いたとしても、単に間違って書いた場合、私の「バグ」をキャッチする目も高く評価されます...

4

2 に答える 2

1

メモリ管理の問題ではありません

NSTimer インスタンスをスケジュールすると、実行ループはタイマーを保持するため、自分で保持することを心配する必要はありません (ARC かどうか)。

タイマーもそのターゲットを保持するため、ターゲットが最初に割り当て解除される状況を心配する必要はありません。ただし、ターゲットがビューの場合は、ビュー階層から削除された後にタイマー メッセージを受け取ることがあります。その場合、メッセージを無視する必要があります。

これは、次の 2 つの方法で処理できます。

  1. actionMethodで、アクションを引き続き実行することを確認してください。そうでない場合は、メッセージを無視して、すべてをクリーンアップします。

  2. タイマー インスタンスへの参照を保持し、[timer invalidate]タイマー メッセージを受信する必要がなくなったときに呼び出します。

ターゲットが常に近くにいて、いつでもメッセージを受信して​​も問題ないと確信できる場合は、何もする必要はありません。たとえば、アプリ デリゲート オブジェクトは、アプリケーションの存続期間中存在します。

NSTimerの対象アクション

任意のオブジェクトの任意のメソッドだけを呼び出すようにタイマーを設定することはできません。タイマー メソッド は、メッセージを送信したNSTimerインスタンスである 1 つのパラメーターのみを取る必要があります。実際にコンパイルして実行するactionMethod:を取得するように定義しましたが、タイマーが起動してメソッドが呼び出されると、 の値は NSTimer オブジェクトのメモリ アドレスになります。これはあまり役に立ちません。intsomeDataINeed

この方法でメソッドを定義する必要があります。

- (void)actionMethod:(NSTimer *)timer

値を取得するsomeDataINeedには、タイマーの userInfo に NSNumber として保存できます。タイマーにデータを追加できるようにするためだけに存在します。

    NSNumber *number = [timer userInfo];
    int someDataINeed = [number intValue];

タイマーを作成するときは、 をnextDataINeedNSNumber にラップし、userInfo を次のように設定できます。

    currentTimer = [NSTimer scheduledTimerWithTimeInterval:nextFire 
                            target:self 
                            selector:@selector(actionMethod:)
                            userInfo:[NSNumber numberWithInt:nextDataINeed]
                            repeats:NO];

userInfo はオブジェクトでなければならないため、NSNumber を使用する必要があります。

間違いの可能性

上記を適用しても問題が解決しない場合は、基準日以降ではなく、現在aMethodThatCalculatesTheNextFiringTimeからの時間間隔を返していることを確認してください。タイマーを 3 秒で始動させたい場合、 の値は でなければなりません。誤って電話をかけると膨大な数になり、タイマーは数年後に起動するようにスケジュールされます.nextFire3.0timeIntervalSinceReferenceDate

于 2012-08-13T16:33:24.977 に答える
1

ARCを使用していると思います。タイマーを強力な変数に保存しないため、起動する前に解放されます。ivar「NSTimer * aTimer」を作成し、タイマーをインスタンス化するときにローカル変数の代わりにそれを使用します(runLoopがスケジュール時にそれを保持しているかどうか思い出せないので、ここで困惑しています-おそらく、完全を期すためにここに残します。何らかの理由で「ポップ」された場合などは、タイマーで無効化を呼び出す必要があります)。

また、タイマーは次のメッセージを送信します: actionMethod:(NSSTimer *)itself ですが、アクション メソッドには int が必要です。それを調整する必要があります。

編集:

actionMethod を次の形式に変更します。

-(void)actionMethod:(id)something
{
  int foo = 0;
  if([something isKindOfClass:[NSNumber class]]) {
    foo = [foo intValue];
  } else
  if([something isKindOfClass:[NSTimer class]]) {
    foo = ... // however you get your value
  } else
     assert(!"Yikes! Wrong class");
  }
  ...

次に、NSNumber を送信するか、タイマーでタイマーを呼び出すことができます。NSNumber から int を引き出すか、タイマーから値を取得できます。

于 2012-08-12T17:13:54.650 に答える