複数の非同期処理を前の処理結果を受けとりつつ数珠つなぎに直列に書きたかった時に参考にしたコード

以前

@xcatsan さんのこちらの記事をさっきみてて思い出したので備忘録として。

以前、非同期で実行される処理の塊を直列に順序を決めて実行したいというシチュエーションがありました。そのときにぱっと浮かんだのは昨日の Cocoa 勉強会関西で@yashigani が発表した内容 にも登場した NSOperationNSOperationQueue でした。「非同期で実行される処理の塊を直列に順序を決めて実行」だけならそれでも良かったんですが、その処理の塊の結果を次の処理の塊に受け渡す必要がありました。さらにその処理結果によって次以降に実行する処理の塊も振り分けるとか…。基本的に非同期処理のコールバックは Blocks を使うことは僕の中できまっていたので Blocks の入れ子とか地獄なのは分かっていたのでどうにかしたかった。

やりたかったこと要求されてたこと

  • UI から1つのタスク(大)が依頼される
  • そのタスク(大)は一個のメソッドとして実装され、 completionBlock でその結果を UI に返される
  • タスク(大)は実は複数のタスク(中)を数珠つなぎにしたもの
  • たまにタスク(大)はタスク(中)の実行状況に合わせてタスク(中)を追加したりする
  • 実はタスク(中)もタスク(小)の集合だったりする

参考にしたライブラリ

前振りが無駄に長かったですがそのときに参考にしたライブラリを列挙します。

STDeferred

今回の要求を聞いて最初に思い浮かんだのがこのライブラリ。処理結果を次の結果に受け渡しています。 1つのメソッドに Blocks を数珠つなぎになる感じだったのでそのまま使うことはなかったですが、受け渡しかたの参考にさせていただきました。

Sequencer

冒頭にあげた @xcatsan さんのこちらの記事でも紹介されています。参照先に書かれているとおりシンプルだったので参考になりました。STDeferred よりも書き方がシンプルに見えて好みでした。

AAMCommandKit

Serial 実行する Command とか参考にさせてもらいました。

結局こんな実装に

コードは諸事情で出せないのですが、うろ覚えの実装はこんな感じだったです。

  • NSOperationNSOperationQueue っぽいものを自作
  • オレオレ Operation クラスには前の処理結果を取り出すプロパティと自分の処理結果を入れるプロパティを設けた
  • オレオレ Queue は突っ込まれた Operation を実行し、キューイング時に、実行済みの Operation の処理結果を取り出して次の Operation の previousResult 的なプロパティに突っ込んでから次の Operation を実行
  • オレオレ Queue には Delegate が実装してあり Operation をキューイングする時に前の Operation と次の Operation を垣間見れてフックしたりもできる
  • Operation 内部では execute されたときに previousResult を見つつその内容によって実行内容を調整
    • 例えば前の処理がエラーだったら、そのエラーを自身の結果として詰め込むとか。つまり、1, 2, 3, 4 と処理が並べてあっても、もし 2 でこけてたらそのまま 2 がコケたよという結果をそのまま伝搬することで最終的に結果をコールバックされた呼び出し元(タスク(大)の呼び出し元)では誰がコケたのかわかる。
  • Queue には全 Operation の実行が完了したことを伝える Delegate を実装

懺悔

参考にしたライブラリを複雑そうだ思ったりもした割に、自分の実装の方がはるかに複雑です。ごめんなさい。

継承とかも入ってたので複雑度マックスです。ごめんなさい。

全体像の把握がツライです。ごめんなさい。

でも、そのプロジェクト内では、新規タスクの追加や仕様変更にはそこそこ強かったのでこれはこれでよかったかなと思ってます。

次回似たような要求があったときにはもっと整理した形で実装したいと思っています。