実行環境
>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 の以下の記事や各種プロポーザルを見て理解を深めた。
知識の定着のために、Thread.isMainThread の値がどうなるかのクイズを5問作ったので解いてみてほしい。
Question 1
答えと解説
答え
true
解説
- UIViewControllerは
@MainActor
が付与されている - MainActorはexecutorがメインのDispatchQueueと同等
@MainActor
はGlobal Actor- 型自体にGlobal Actorの注釈を付けた場合、そのメソッドやプロパティはそのGlobal Actorにisolatedされる
- MainActorにisolatedされたまま実行されるため、メインスレッドで実行される
Question 2
答えと解説
答え
true
解説
Task.init
はactor contextを継承する- MainActorにisolatedされたまま実行されるため、メインスレッドで実行される
Question 3
答えと解説
答え
false
解説
Task.detached
はactor contextを継承しない- MainActorにisolatedされないため、メインスレッドで実行されない
Question 4
答えと解説
答え
false
解説
Task.init
はactor contextを継承するhello()
そのものの呼び出しは継承したactor contextであるMainActorで実行される- しかし、
hello()
自体はどのActorにもisolatedされていないasync functionのため、中身の処理はActorのexecutor上で実行されない - よってメインスレッドで実行されない
Question 5
答えと解説
答え
true
解説
Task.init
はactor contextを継承するhello()
はnonisolatedされているため、任意のActorまたは同期実行の文脈で使うことができるhello()
はsync functionのため、呼び出し元のActorのexecutor上で実行される- MainActorで実行されるため、メインスレッドで実行される
おわりに
Question 4については先週の自分だったら答えと解説をセットで答えられなかった。難しい、というか実行されるActorの意識が無かった。また、メインスレッドで実行したい関数はTask側でMainActorを指定するのではなくて、関数自体 or そのメソッドを持つクラスや構造体に指定した方が良さそう。なぜなら、呼び出し先がasync functionならActorを引き継がないため、呼び出し元でメインスレッドで実行するかどうかを考えなくて済むから。