Upgrade to Pro — share decks privately, control downloads, hide ads and more …

メインスレッドをブロックさせないためのSwift Concurrencyクイズ

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for tokizo tokizo
August 23, 2024
5.6k

メインスレッドをブロックさせないためのSwift Concurrencyクイズ

Avatar for tokizo

tokizo

August 23, 2024
Tweet

Transcript

  1. Swift Concurrencyは規則が多い (1/2) • Actor, Global Actorなどの詳細な規則を知るには 以下を読む・追う必要がある ▪ Swift

    Evolutionのプロポーザル ▪ WWDCのセッション ▪ https://developer.apple.com/documentation/ swift/concurrency/ 12
  2. クイズのルール (2/2) // ✅: 通る → MainActor上、つまりメインスレッドで実行される* // ❌: 通らない

    → MainActor上で実行されない MainActor.assertIsolated() 24 *: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0316-global-actors.md
  3. import UIKit class PracticeViewController: UIViewController { override func viewDidLoad() {

    super.viewDidLoad() MainActor.assertIsolated() // ✅ or ❌ ? } } 26
  4. import UIKit class PracticeViewController: UIViewController { override func viewDidLoad() {

    super.viewDidLoad() MainActor.assertIsolated() // ✅ } } 28
  5. 練習: 解説 (2/3) 31 *: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0316-global-actors.md#using-global-actors-on-a-type **: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0316-global-actors.md#global-actor-inference • クラスに

    @MainActor を付けた場合、メソッド、 プロパティ、サブスクリプトは MainActor に 隔離される* • サブクラスはスーパークラスから Actor の隔離を 推論される**
  6. import UIKit class PracticeViewController: UIViewController { override func viewDidLoad() {

    super.viewDidLoad() MainActor.assertIsolated() // ✅ } } 33
  7. import UIKit class Q1ViewController: UIViewController { override func viewDidLoad() {

    super.viewDidLoad() Task.detached { MainActor.assertIsolated() // ✅ or ❌ ? } } } 39
  8. 41 import UIKit class Q1ViewController: UIViewController { override func viewDidLoad()

    { super.viewDidLoad() Task.detached { MainActor.assertIsolated() // ❌ } } }
  9. 問1: 解説 • Detached Task は呼び出し側の Actor の 一部ではない、非同期に実行できる作業単位* ◦

    → MainActor とは別で実行されるため、 メインスレッドで実行されない 43 *: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/#Unstructured-Concurrency
  10. 44 import UIKit class Q1ViewController: UIViewController { override func viewDidLoad()

    { super.viewDidLoad() Task.detached { MainActor.assertIsolated() // ❌ } } }
  11. import UIKit // 以後省略 class Q1ViewController: UIViewController { override func

    viewDidLoad() { super.viewDidLoad() Task.detached { MainActor.assertIsolated() // ❌ } } } 46
  12. func foo() async { MainActor.assertIsolated() // ✅ or ❌ ?

    } class Q2ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { @MainActor in await foo() } } } 48
  13. func foo() async { MainActor.assertIsolated() // ❌ } class Q2ViewController:

    UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { @MainActor in await foo() } } } 50
  14. 問2: 解説 (1/3) • in の前に @MainActor を付けると、 クロージャの処理が Main

    Actor で実行される* 52 https://github.com/swiftlang/swift-evolution/blob/main/proposals/0316-global-actors.md#closures
  15. 問2: 解説 (2/3) • await foo() は MainActor 上で呼び出される •

    しかし、foo() 自体はどの Actor にも 隔離されていない async function のため、 中身の処理は MainActor 上で実行されない* 53 *: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0338-clarify-execution-non-actor-async.md#detailed-design
  16. @MainActor func foo() async { MainActor.assertIsolated() // ✅ } •

    関数に @MainActor を付けると通る 問2: 解説 (3/3) 54
  17. func foo() async { MainActor.assertIsolated() // ❌ } class Q2ViewController:

    UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { @MainActor in await foo() } } } 55
  18. @propertyWrapper struct AlwaysZero { @MainActor var wrappedValue: Int { return

    0 } } struct Counter { @AlwaysZero var value: Int func f() { MainActor.assertIsolated() /* ✅ or ❌ ? */ } } class Q3ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { await Counter().f() } } } 58
  19. @propertyWrapper struct AlwaysZero { @MainActor var wrappedValue: Int { return

    0 } } struct Counter { @AlwaysZero var value: Int func f() { MainActor.assertIsolated() /* ✅ */ } } class Q3ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { await Counter().f() } } } 60
  20. 問3: 解説 (1/3) • Global Actor が付与された wrappedValue を持つ プロパティラッパーが付与されたプロパティを持つ

    構造体やクラスはそのプロパティラッパーから Actor の隔離が推論される* 62 *: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0316-global-actors.md#global-actor-inference
  21. 問3: 解説 (2/3) • → @AlwaysZero が付与されたプロパティを持つ Counter は MainActor

    に隔離される 63 @propertyWrapper struct AlwaysZero { @MainActor var wrappedValue: Int { return 0 } } struct Counter { @AlwaysZero var value: Int func f() { MainActor.assertIsolated() /* ✅ */ } }
  22. 問3: 解説 (3/3) • Swift 6からは無効になる* ◦ Swift 5.9以降 Upcoming

    Feature Flag が使える ▪ DisableOutwardActorInference 64 *: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0401-remove-property-wrapper-isolation.md
  23. @propertyWrapper struct AlwaysZero { @MainActor var wrappedValue: Int { return

    0 } } struct Counter { @AlwaysZero var value: Int func f() { MainActor.assertIsolated() /* ✅ */ } } class Q3ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { await Counter().f() } } } 65
  24. @MainActor class Animal { deinit { MainActor.assertIsolated() // ✅ or

    ❌ ? } } class Q4ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { await Animal() } } } 68
  25. @MainActor class Animal { deinit { MainActor.assertIsolated() // ❌ }

    } class Q4ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { await Animal() } } } 70
  26. 問4: 解説 • deinit は Global Actor に隔離されない* • 2024/08/21現在、isolated

    deinit の プロポーザル** が Accept されている*** 72 *: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0316-global-actors.md#detailed-design **: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0371-isolated-synchronous-deinit.md ***: https://forums.swift.org/t/accepted-with-modifications-se-0371-isolated-synchronous-deinit/74042
  27. @MainActor class Animal { deinit { MainActor.assertIsolated() // ❌ }

    } class Q4ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { await Animal() } } } 73
  28. protocol P { @MainActor func f() } struct Y: P

    {} extension Y { func f() { MainActor.assertIsolated() /* ✅ or ❌ ? */ } } class Q5ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { Y().f() } } } 76
  29. protocol P { @MainActor func f() } struct Y: P

    {} extension Y { func f() { MainActor.assertIsolated() /* ❌ */ } } class Q5ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { Y().f() } } } 78
  30. 問5: 解説 (1/3) • SE-0316* 内のコードをほぼそのまま使用 • プロトコルの実装を準拠とは別に extension で

    行うと、そのプロトコルが要求する Global Actor を 引き継がない 80 *: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0316-global-actors.md#global-actor-inference
  31. protocol P { @MainActor func f() } struct Y: P

    {} extension Y { func f() { MainActor.assertIsolated() /* ❌ */ } } class Q5ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { Y().f() } } } 83
  32. クイズ まとめ • 問1: SE-0304 Detached Task • 問2: SE-0338

    Actor 隔離されていない非同期関数 • 問3: SE-0401 プロパティラッパーからの推論 • 問4: SE-0316 Global Actorの規則 • 問5: SE-0316 Global Actorの規則 86