iOSのマルチスレッド入門・UI編

iOSアプリでまあ簡単なアプリだったら良いんですけど、いくつか画面があったり メインスレッド以外で処理をしてなるべくユーザの操作をブロックしたくないってケースは当然あると思います。

最近作ってるアプリでマルチスレッド対応をしたので備忘録というかまあメモ的な感じで残しておきます。 ここに残してある方法以外にも並列プログラミングの方法や必要なシーンなどはたくさんあるので (ネットワーク通信とかいろいろ)そこはまた機会があれば書きます。

NSTimerを使う

タイマーはゲームを作ったりといったシーンなどでも使いたいんじゃ無いでしょうか。 NSTimerを使うことで実現できます。以下の様に実装するだけです。

タイマーを起動し、指定した間隔で処理を実行する

self.timer = [NSTimer
    scheduledTimerWithTimeInterval:1.0f
    target:self selector:@selector(updateText:)
    userInfo:nil repeats:YES];

タイマーを停止する

if (self.timer && [self.timer isValid]) {
    [self.timer invalidate];
}

タイマーを開始(再開)する

if (self.timer && ![self.timer isValid]) {
    [self.timer fire];
}

非常に簡単ですね。 例えばゲームなどでタイムオーバ後に次の画面でスコアを表示したい場合などは、 タイマーで時間をカウントし、必要なタイミングでタイマーを停止->次の処理とすればいいですし、 広告などでタイマーを停止した場合は復帰時にfireで戻してあげるなどすればいいかと思います。

NSThreadをつかう

NSTimerよりもこちらの方が汎用的に使えるので利用シーンは多いと思います。 画像ビュアーなどで画像読み込み中に別の画像をスワイプして表示させたり、 webからデータを読み込んでいるけどユーザの操作は止めたく無いといった場合に必要になります。

別threadを起動して処理を実行する

[NSThread
    detachNewThreadSelector:@selector(asyncLoadData:)
    toTarget:self
    withObject:userinfo];

NSThreadで起動したスレッドはautorelease poolを自分で用意する必要があります autoreleasepoolブロックを利用するだけでいいらしいです。

- (void) asyncLoadData:(id)userinfo {
    @autoreleasepool {
        UIView *hogeView = [[UIView alloc] initWIthFrame:...] autorelease];
        ...
    }
}

複数のスレッドの排他処理を行いたい場合はsynchronizedブロックを利用すると簡単に実装できます

@synchronized (self) {
    self.atomicProp = ...; @synchronizedブロックは同時に実行できない
}

細やかな排他処理を記述することも当然可能です

NSLoc *lockObj = [NSLock alloc] init];
[lockObj lock];   // 一度lockされるとunlockされるまで他の処理は休止する
[lockObj unlock]; // unlockは同じスレッドで行う必要がある

以上、今回自作のアプリで対応した方法のさらっとした紹介でした。 これらは主にUIまわりで利用できると思います。