iOSのアプリを作っている時に定期的にメソッドを実行したい場合があると思います。

例えば、

  • ある画面に何分以上いたら「シェアしてよ」のアラートを出す
  • ある一定時間同じ画面にいたら画面描画を変える

などの場合です。

そんな時はNSTimerを使うとより簡潔に書けます。

ということでNSTimerの紹介をします。

NSTimer

You use the NSTimer class to create timer objects or, more simply, timers. A timer waits until a certain time interval has elapsed and then fires, sending a specified message to a target object. For example, you could create an NSTimer object that sends a message to a window, telling it to update itself after a certain time interval.

毎度のように意訳になりますが、

「このタイマーは発火してからある一定時間が経過するまで待ったのち、ターゲットオブジェクトに指定されたメッセージを送ります。」

かなと。

大事な部分だけをはしょって紹介しました。

多分みなさんが普段違う言語で使うTimerクラスと出来る事はそんなに変わらないです。

ちなみにこのNSTimerクラスではメインスレッドでも別スレッドでも動作させるようにすることが出来ます。

一つのNSTimerと複数のNSTimerを使うのとで使い方が変わるので、別々に紹介したいと思います。

一つのNSTimerしか登録しない場合

AndroidiOSのUI周りはシングルスレッドというのはとっても有名なお話だと思います。

メインスレッド以外からUI操作(例えばViewの追加や操作)をするとアプリがクラッシュします。

1
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats

このメソッドは呼び出した後、自動でタイマーをメインスレッド内でスタートします。

そのため、特に生成した後にメソッドを呼んだりする必要はありません。

また生成したスレッド内でタイマーが作動する用になっています。

このメソッドを使ってNSTimerを使用します。

パラメーターは以下になります。

  • (NSTimeInterval)secods

    何秒後に実行するかの秒数。この値が0.0以下である場合、代わりに0.1ミリ秒が渡される。

  • (id)target

    targetに指定されたオブジェクトに、指定された秒数後に指定されたメッセージを送ります。

  • (SEL)aSelector

    targetに送るメッセージ。

  • (id)userInfo

    タイマーに付加するユーザー情報。タイマーが無効化されるまでタイマーはこのオブジェクト(userInfo)への強い参照を保持している。

  • (BOOL)repeats

    YESの場合、タイマーが無効化されるまで繰り返します。NOの場合、一度メッセージを送った後、タイマーは無効化になります。

という形です。

1
2
3
4
5
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(repeateMethod:) userInfo:nil repeats:YES];
- (void)repeateMethod:(NSTimer *)timer
{
NSLog(@"Hoge");
}

とすれば1秒ごとにrepeateMethod:(NSTimer *)timerが実行されます。

(id)userInfoを渡した場合は

1
2
3
4
5
6
7
NSDictionary *userInfo = @{@"message": @"Hoge"};
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(repeateMethod:) userInfo:userInfo repeats:YES];
- (void)repeateMethod:(NSTimer *)timer
{
NSDictionary *userInfo = (NSDictionary *)[timer userInfo];
NSLog(@"%@", userInfo[@"message"]);
}

という形になります。

- (SEL)aSelectorに渡すメソッド

第3引数の- (SEL)aSelectorに渡すセレクタは次のシグネチャを持つ必要があります。

1
@selector(timerFireMethod:)

またそのメソッドには以下のパターンでなければなりません。

1
2
3
- (void)timerFireMethod:(NSTimer *)timer
{
}

引数には自分自身を渡しています。

複数のNSTimerを同時に使う場合

複数のNSTimerを同時に使う場合は

  1. まずはNSTimerを生成する
  2. NSRunLoopにNSTimerをセットする

のような流れになります。

まず以下のメソッドを使って、NSTimerを生成します。

1
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds target:(id)target selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats

各引数は上記のものと同じなので省略させて頂きます。

このメソッドと先程のメソッドとなにが違うかというと、タイマー処理をスタートさせるかどうかです。

このメソッドは勝手にタイマーはスタートしません。

そしてNSRunLoopを生成します。

1
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

そしてNSTimerを以下のメソッドを使って追加します。

1
- (void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode

具体的には以下のソースコードで出来ます。

1
2
3
4
5
6
7
8
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(repeatMethod:) userInfo:nil repeats:YES];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSRunLoopCommonModes]
- (void)repeateMethod:(NSTimer *)timer
{
NSLog(@"Hoge");
}

処理は別スレッドにしてその後メインスレッドで処理がしたい

そんなわがままな人向けです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(repeatMethod:) userInfo:nil repeats:YES];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSRunLoopCommonModes]
- (void)repeateMethod:(NSTimer *)timer
{
// なにかの処理
NSLog(@"Hoge");
[self performSelectorOnMainThread:@selector(mainThreadMethod) withObject:nil waitUntilDone:YES];
}
- (void)mainThreadMethod
{
// メインで行いたい処理
}

というようにすればメインスレッドでの処理も可能になります。

つまり、別スレッドで一定間隔で処理をしてから、メインスレッドでUI操作も出来るということです。

終わりに

駆け足になってしまいましたが、このような形で定期処理を実行できます。

ストップウォッチとかを作るには最適なクラスだと思うので、ぜひ使ってみてください。

以上になります。

参考

NSTimer Class Reference | Mac Developer Libarary

NSRunLoop Class Reference | iOS Developer Library

iOS

「iOS」のおすすめ記事