Objective-CなどiOS関連の技術メモ。知識はここに投げ捨てて忘れる。

NSOperationの使い方

最終更新日 2013年07月21日 12:42

NSOperationの解説にはすこし混乱があり紛らわしい部分がある。

使い方の大雑把な分類

まず、NSOperationの使い方は、大きく2通りに分類される。

  • NSOperationQueueと組み合わせて使う
  • 単独で使う

NSOperationQueueとはセットで語られることが多いが、最初にこの大分類があることを理解しておこう。

そして、そのうち単独で使うパターンにおいては、NSOperationは更に2つに分類できる。それが並列[Concurrent]か非並列[Non-Concurrent]かである。

NSOperationQueueに詰めて使う

ここで一旦、単独で使う場合の話は脇に置いて、NSOperationQueueの話を先にしておく。NSOperationQueueと組み合わせて使う場合は、キューの選び方で並列か非並列かが決まる。具体的にいえば、[[NSOperationQueue alloc] init]で作成したキューに詰め込めばNSOperationオブジェクト自体の実装にかかわらず並行処理されるし、[NSOperationQueue mainQueue]で取得したキューに詰め込めば、処理するのはメインスレッドだけだから、非並列になる。NSOperationQueueに詰めて実行する場合、NSOperation自体の並列・非並列という概念は意味をもたない(※1)。

キューに詰め込むNSOperationオブジェクトには、具象サブクラスであるNSBlockOperationあたりを使っておけば普通は事足りるはずなので、敢えて自分でNSOparationのサブクラスを作成する必要はないと思うが、作る場合は、mainメソッドをオーバーライドするだけでよい。

※1 OS X v10.5以前ではすこし状況が異なり、「NSOperationのisConcurrentがNOを返す場合(デフォルトはNO)のみ、NSOperationQueueは新しいスレッドを作成する(=並列処理する)」とAppleのドキュメントにある。意味的に逆じゃね?と思いつつ、検証するのも面倒なので、引用だけしておく。

Note: In OS X v10.6, operation queues ignore the value returned by isConcurrent and always call the start method of your operation from a separate thread. In OS X v10.5, however, operation queues create a thread only if isConcurrent returns NO. In general, if you are always using operations with an operation queue, there is no reason to make them concurrent.

NSOperation Class Reference

単独で使う

さて、単独で使う場合。単独でとは、NSOperationQueueを使わず、自分でNSOperationのstartメソッドを呼び出すということ。先に述べたように、「並列」と「非並列」に分類できるわけだが、このうちの「非並列」は、形式上一応分類されているものの、あんまり存在意義のあるシロモノではない。Appleのドキュメントから非並列オペレーションに関する部分を引用する。

If you plan on executing an operation object manually, instead of adding it to a queue, you can design your operation to execute in a concurrent or non-concurrent manner. Operation objects are non-concurrent by default. In a non-concurrent operation, the operation’s task is performed synchronously—that is, the operation object does not create a separate thread on which to run the task. Thus, when you call the start method of a non-concurrent operation directly from your code, the operation executes immediately in the current thread. By the time the start method of such an object returns control to the caller, the task itself is complete.

オペレーションをNSOperationQueueを使わず手動で実行する場合、並列または非並列いずれかの設計を選択できる。オペレーションはデフォルトでは非並列で、その場合、処理は同期実行される。オペレーションがタスクを実行するのに新しいスレッドを立ちあげることはない。非並列オペレーションのstartメソッドを直接呼び出すと、そのオペレーションは現在のスレッド上で即座に実行される。そして、startメソッドが呼び出し元に制御を戻すまでに、処理は完了する。

NSOperation Class Reference

くどくど説明しているが、要するに、同期された普通のメソッド呼び出しなのである。先ほど、NSOperationQueueに詰めて使うタイプのNSOperation実装のひな形コードを示したが、それをそのまま単独でつかうと、非並列オペレーションになる。多分、そのような「キューを使って並列実行する用のオペレーションを何らかの理由で同期実行する」ようなケースでしか使わないだろう。なお、実装するのはmainメソッド、呼び出すのはstartメソッドであるが、その性質から考えて、直接mainメソッドを呼び出しても同じことだと思われる。

そして、最後に「並列」オペレーション。くり返しになるが、ここでいう並列とは、NSOperationQueueを使わなくても並列処理される、ということである。NSOperationサブクラスの実装についてはネット上でも比較的多くの人がブログなどで言及している(isExecutingやisFinishedを実装して云々・・・というやつ)が、それはこの、単独利用で並列処理する場合の話である。NSOperationサブクラスの実装方法については、ネット上にたくさんのドキュメントがあるので、今回ここで繰り返すのはやめておこうと思う(めんどくさい)。

まとめ

Appleのドキュメントに「Concurrent Versus Non-Concurrent Operations」などと大きなトピックで取り上げているのが勘違いの元だと思うが、非並列オペレーションはそんな名前をつけるほど大それた概念ではない。要するに、実用的なところで言えば、

  • 非並列・同期であるかのように実装したオペレーションをNSOperationQueueで並列・非同期で実行する
  • オペレーション自体を並列・非同期で実装し、NSOperationQueueを使わず直接startメソッド呼び出しで実行する

という当たり前のような2つの選択肢があるにすぎない。

詳解 Objective-C 2.0 第3版

必読。こんなゴミサイトより、こっちを読むべき。

Amazonでみる

iPhoneプログラミングUIKit詳解リファレンス

必読。こんなゴミサイトより、こっちを読むべき。

Amazonでみる