Closed5

Swift Strict Concurrency 対応のメモ

山田良治山田良治

classの代わりにactor使うとテストでスタブ/モックの注入できなくない?継承によるテストダブルの実現が封じられてしまう。

山田良治山田良治

Swift 6 への移行セッションでもLocationManagerとかはMainActorにしてたけど、xxContainerとかの「テストでサブクラスしたそうやけど、参照セマンティクスの必要そうなもの」は、classを維持しつつ、MainActorに準拠させるのが良さそう〜。

https://developer.apple.com/wwdc24/10169

山田良治山田良治

そもそもManager系クラスって、俺は恥ずかしながらスレッドセーフをあまり気にせず使ってたけど、これまで大概はメインスレッドから呼ばれていたはずだし、スレッドセーフを考えている方でもメインスレッドからのアクセスに限定するとかで、今はMainActorにしても9割5分のケースで何の問題もないと思うので、基本的にそうしちゃおうか
(ViewとかUIViewがMainActorなので、これはUIレイヤが適応するものでは?と構えちゃうのが良くないかも)

プライベートキューを使って排他制御を実装してた人も、今はactorをマークするだけで良いし。

あと@MainActorをつけていても、その中でawaitキーワードをつけてメソッドを呼び出すと、明示的にMain Actorに隔離していない場合は別スレッドで動作するので、そこは安心して良い。

山田良治山田良治

個人的なStrict Concurrency対応方針

基本

  • 不必要なvarletにする
  • 基本的には値型を使う
  • グローバル変数には注意する
  • 自前で排他アクセスの仕組みを持っている場合はSendableをマークする

応用

  • 上記で対応しきれないclassは基本的に@MainActorをマークする. @MainActor classは継承可能なので継承によるスタブ/モックの実現も楽にできる(重たい画像処理やAPIアクセスなどの非同期処理にすべきものを該当クラスでベタ書きしてないか気をつける.)
  • 特に継承によるテストダブルが必要ではない・独自スレッドで動作してほしい・参照セマンティクスが欲しい。が揃っていたらactorを使う
  • 外部モジュールや、まだ自分の実装がどうしてもStrict Concurrencyに対応できない場合はpreconcurrencyMainActor.assumeIsolated@unchecked Sendableを活用する
  • Swiftのasync/awaitはActor Reentrancyなので、サスペンド中でも同じメソッドが呼ばれうる。値を更新するようなメソッドは基本的に同期実装にしておけばセーフティ。もしくは、値を更新する箇所の処理を工夫して他の処理が並行して呼ばれていても問題ないようにしておく。
山田良治山田良治

俺が「何の抽象化にもなっていないテスタビリティのためだけのインターフェース分離」が嫌いで、シンプルな継承とイニシャライザによる依存性注入でテストダブルを実現するのを好むので、1個目のコメントみたいな悩みが出たのかもしれない。

このスクラップは4ヶ月前にクローズされました