Developer's Blog

メソッドの遅延実行

あるメソッドを一定時間後に実行したい、ということはよくあります(これを、メソッドの遅延実行と呼ぶことにします)。そんなときにまず思いつくのは NSTimer を使う方法ではないでしょうか。

NSTimer を使う以外にも、NSObject にある次のメソッドで簡単に同じことができます。

NSObject
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay

例として、iPhone 標準搭載の写真アプリを見てみましょう。

写真アプリでは、写真を見ている間に一定時間ユーザの操作が無ければ、ツールバーとナビゲーションバーが消えます。

この処理は次のようなコードで実現できます。

// self は UIViewController
…
{
…
  // 一定時間後にバーを消すメソッドを登録する
  [self performSelector:@selector(hideBar) withObject:nil afterDelay:5];
…
}

- (void)hideBar
{
  // ツールバーとナビゲーションバーを消す処理
}

簡単ですね!

NSTimer を使う場合は、この例よりもう少しコードが長くなります。NSTimer の方が機能は豊富ですが、この程度の処理なら
performSelector:withObject:afterDelay: の方が簡単でおすすめです。

NSTimer と performSelector:withObject:afterDelay: の違い

NSTimer と performSelector:withObject:afterDelay: にはそれぞれメリット/デメリットがあるので、場合に応じて使い分けが必要です。

どのような違いがあるのかを見てみましょう。

NSTimer

  • メリット
    • 指定したメソッドを繰り返し呼び出す機能がある。
  • デメリット
    • performSelector:withObject:afterDelay: より、必要なコードが長くなる。
    • 実行をキャンセルしたい場合、 NSTimer インスタンスを保持しておく必要がある。

performSelector:withObject:afterDelay:

  • メリット
    • NSTimer より、必要なコードが少ない。
    • 実行をキャンセルしたい場合、何も保持する必要が無い。
  • デメリット
    • 繰り返し実行ができない。

繰り返し実行

performSelector:withObject:afterDelay: には繰り返し実行の機能が無いので、繰り返しメソッドを呼び出したい場合、呼び出されたメソッド内でもう一度 performSelector:withObject:afterDelay: を呼び出す必要があります。

NSTimer には繰り返し実行を行うオプションがあるので、これをを利用した方が見た目に素直な実装になるでしょう。

実行のキャンセル

先ほどの写真アプリの例に戻りましょう。

バーを消すメソッドを遅延実行登録した後、メソッド呼び出されるまでの間にツールバーのボタンが押されたとします。

この場合、先に登録したバーを消すメソッドが呼び出されてしまうと困るので、登録をキャンセルする必要があります。

NSTimer でキャンセルする場合、 NSTimer のインスタンスをインスタンス変数として保持する必要があります(NSTimer インスタンスの invalidate メソッドを呼び出す必要があるため)。

でも、管理しなければならないインスタンス変数は少ない方がうれしいですよね。performSelector:withObject:afterDelay:で登録したメソッドの実行をキャンセルしたい場合は NSObject にある次のクラスメソッドを利用します。これならインスタンス変数に何も保持しなくても OK です。

NSObject
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument
// インスタンスメソッドではなくクラスメソッドであることに注意

上記 2つはそれぞれ次のような違いがあります。

  • ターゲットに関連づいたすべての登録をキャンセルする
  • ターゲットに関連づいた登録のうち、セレクタ/引数が一致するものをキャンセルする

複数のメソッドを同時に登録していて、そのうちのいくつかをキャンセルしたいという場合は後者を使用します。

Copyright © 2019 Fenrir Inc. All rights reserved.