Swift Concurrency メインスレッドクイズ(2) 2問

実行環境

>swift --version
swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0

>xcodebuild -version
Xcode 15.3
Build version 15E204a

モチベーション

まだまだSwift Concurrencyパワーが足りていない。読み、書き、動かし、クイズを作り、Swift Concurrencyを身体に馴染ませる。

過去のクイズは以下。

Question 1

gist.github.com

答えと解説

答え

false

解説

前提として、Detached Taskは実行元のactor contextを引き継がないので、ViewControllerに暗黙的に付与されているGlobal ActorであるMain Actorのexecutor上で y.f() は実行されない。

0316-global-actors.md に載っている例だが、説明が初見で分からなったので整理した。

プロポーザルに出てくるwitnessというのは特定のプロトコル準拠内でその要件を満たすために使用される定義のこと。プロポーザルを読むと、プロトコルのメソッド実装がそのプロトコルに適合する宣言と同じ型定義やextension内にある場合のみ、actor isolationの推論が有効になるようで、今回は YP の準拠と YP の要件実装が別のためactor isolationの推論が有効にならず、P のメソッド定義に付与されている @MainActorY で有効にならない。YP の準拠と Y の要件実装を同じ箇所で行うと推論が効くとのことなので試すと、trueになった。

gist.github.com

これまでの説明を踏まえてコードを振り返ると、y.f() の前にawaitキーワードを書くことを要求されなかったので、Main Actorにisolatedされていないことが分かる。

Question 2

gist.github.com

答えと解説

答え

true

解説

0316-global-actors.md にて、Global Actorが付与された wrappedValue を持つプロパティを含む構造体やクラスはそのProperty Wrapperからactor isolationの推論が行われるようになった。

@TwelveOrLess Property Wrapperの wrappedValue はMain Actorにisolatedされているため、それを持つ構造体 SmallRectangle はMain Actorにisolatedされる。SmallRectangle のメソッドもMain Actorにisolatedされるためメインスレッドで実行される。呼び出し時に await が必要になるのでヒントがあった。

なお、 0401-remove-property-wrapper-isolation にて、Swift 6 language modeではこの推論は行われなくなるためfalseになる。コンパイル時に -enable-upcoming-feature DisableOutwardActorInference を渡すとfalseになることを確認した。今回の実行環境であるSwift 5.10ではデフォルトでその機能は有効になっていないためtrueとなる。

Swift Concurrency 何秒かかる?クイズ 3問

実行環境

>swift --version
swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0

>xcodebuild -version
Xcode 15.3
Build version 15E204a

モチベーション

tokizuoh.hatenablog.com

以前Swift Concurrencyメインスレッドクイズを書いたが、まだまだ理解が浅いなと思って関連のWWDCのセッションを見返したりswift-evolutionを読んでクイズを新たに作った。クイズ作ると定着している感じがあって良い。今回は前回と比べて簡単め。

Question 1

gist.github.com

答えと解説

答え

約7秒

解説

await キーワードによって処理が終わるまで下には進まずに上から順に実行される。よって、Task.sleep(nanoseconds:) の秒数の合計の7秒となる。

Question 2

gist.github.com

答えと解説

答え

約5秒

解説

async let バインディングawait キーワードによって、戻り値が代入されるタイミングまで処理を待つため、Task.sleep(nanoseconds:) の秒数の最大値の5秒となる。

Question 3

gist.github.com

答えと解説

答え

約0秒

解説

await キーワードが無く、 Task.sleep(nanoseconds:) の完了を待たずにメソッドの処理が終わってしまうため、0秒となる。

Swift Concurrency メインスレッドクイズ 5問

実行環境

>swift --version
swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
Target: arm64-apple-macosx14.0

>xcodebuild -version
Xcode 15.3
Build version 15E204a

モチベーション

コードレビューをしている時に、viewWillTransition(to:with:)内で行う表示関係の処理はそのメソッド自体がメインスレッドで呼ばれるから、明にメインスレッドでやるように書かなくても良いのでは?という話題があった。その時は根拠が無くて体感レベルの理解だったが、体感でコードを書きたくないので、id:maiyama4 の以下の記事や各種プロポーザルを見て理解を深めた。

zenn.dev

知識の定着のために、Thread.isMainThread の値がどうなるかのクイズを5問作ったので解いてみてほしい。

Question 1

gist.github.com

答えと解説

答え

true

解説

Question 2

gist.github.com

答えと解説

答え

true

解説

Question 3

gist.github.com

答えと解説

答え

false

解説

Question 4

gist.github.com

答えと解説

答え

false

解説

Question 5

gist.github.com

答えと解説

答え

true

解説

おわりに

Question 4については先週の自分だったら答えと解説をセットで答えられなかった。難しい、というか実行されるActorの意識が無かった。また、メインスレッドで実行したい関数はTask側でMainActorを指定するのではなくて、関数自体 or そのメソッドを持つクラスや構造体に指定した方が良さそう。なぜなら、呼び出し先がasync functionならActorを引き継がないため、呼び出し元でメインスレッドで実行するかどうかを考えなくて済むから。