hachinoBlog

hachinobuのエンジニアライフ

メソッドの遅延実行はperformSelectorでなくdispatch_afterを使う

経緯

メソッドの遅延実行に今までperformSelector: withObject: afterDelay: を使用していたのだが呼び出すメソッドの引数が複数ある場合に困っていた。

呼び出すメソッドの引数を配列で受け取るようにとかすれば良いのだろうけど、そのためだけに仕様を変更したくない。

そんな時に発見したのがGCDのdispatch_after

サンプル

下記のように引数を複数もつメソッドを遅延実行したい場合、performSelector: withObject: afterDelay:では呼び出すことができない。

- (void)nameInfoWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName andNickName:(NSString *)nickname
{
    NSLog(@"firstName:%@", firstName);
    NSLog(@"lastName:%@", lastName);
    NSLog(@"nickname:%@", nickname);
}
//クラッシュする
[self performSelector:@selector(nameInfoWithFirstName:andLastName:andNickName:) withObject:@"hachinobu" afterDelay:2.0f];

- (void)nameInfoWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName andNickName:(NSString *)nickname
{
    NSLog(@"firstName:%@", firstName);
    NSLog(@"lastName:%@", lastName);
    NSLog(@"nickname:%@", nickname);
}

dispatch_afterを使えば

double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [self nameInfoWithFirstName:@"Takahiro" andLastName:@"Nishinobu" andNickName:@"hachinobu"];
});

- (void)nameInfoWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName andNickName:(NSString *)nickname
{
    NSLog(@"firstName:%@", firstName);
    NSLog(@"lastName:%@", lastName);
    NSLog(@"nickname:%@", nickname);
}

出力ログ

firstName:Takahiro

lastName:Nishinobu

nickname:hachinobu

このように柔軟に呼び出すことができる。

結論

調べたところperformSelector系はメモリ管理で危険なところがあるみたい。

当記事のように引数の制限や戻り値にも制限があるためperformSelectorでの遅延実行でなくdispatch_afterを積極的に使っていこうと思う。