Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
メインスレッドをブロックさせないためのSwift Concurrencyクイズ
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
tokizo
August 23, 2024
5.8k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
メインスレッドをブロックさせないためのSwift Concurrencyクイズ
tokizo
August 23, 2024
More Decks by tokizo
See All by tokizo
徐々に増えているSwiftUI Anchor関連API
tokizuoh
1
1.1k
iOS 曜日ランキングが出来上がるまで
tokizuoh
0
2.7k
運動モチベーション継続のためのiOSアプリ開発
tokizuoh
0
1.1k
Xcode 15 で swift run が遅い
tokizuoh
0
3.8k
モノレポにおける path-filtering利用時でも GitHub ステータスのRequiredを 機能させたい!
tokizuoh
1
1.6k
累積和で配列の処理効率を改善しよう
tokizuoh
0
730
Featured
See All Featured
Six Lessons from altMBA
skipperchong
29
4.3k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
140
Rebuilding a faster, lazier Slack
samanthasiow
85
9.5k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
2
1.1k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Thoughts on Productivity
jonyablonski
76
5.2k
Designing for Performance
lara
611
70k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
2k
The SEO identity crisis: Don't let AI make you average
varn
0
480
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.2k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
210
Transcript
メインスレッドをブロック させないための Swift Concurrency クイズ tokizo / @tokizuoh 2024/08/23 iOSDC
Japan 2024 1
README • tokizo ◦ @tokizuoh • 株式会社はてな ◦ マンガアプリチーム 2
3 メインスレッドをブロック させないための Swift Concurrency クイズ
4 メインスレッドをブロック させないための Swift Concurrency クイズ
5 メインスレッドを 適切に使う
メインスレッドを適切に使う (1/2) • 適切さ ◦ メインスレッドで実行が求められる処理のみを メインスレッドで実行する ▪ UIの更新など ◦
メインスレッドでやらなくて良い処理を バックグラウンドスレッドに逃がす 6
メインスレッドを適切に使う (2/2) • 適切に使わないと... ◦ UIの応答性の低下 ◦ クラッシュ ▪ ユーザー体験の低下
▪ → ユーザー離脱・売上減少 ▪ → ブランドイメージの低下 7
8 メインスレッドを適切に使っ てアプリの品質を高めたい
9 しかし Swift Concurrencyは 難しい
10 Swift Concurrencyの 難しさとは
11 Swift Concurrencyは 規則が多い
Swift Concurrencyは規則が多い (1/2) • Actor, Global Actorなどの詳細な規則を知るには 以下を読む・追う必要がある ▪ Swift
Evolutionのプロポーザル ▪ WWDCのセッション ▪ https://developer.apple.com/documentation/ swift/concurrency/ 12
Swift Concurrencyは規則が多い (2/2) • 追う必要性 ◦ Swift のバージョンによって規則が変わる 13
14 完全に理解するのは大変
15 規則を理解していないと 意図せず メインスレッドを 使ってしまう
16 今回のトークの クイズを解いて 規則を知ろう
17 トークのゴール
トークのゴール (初級者) • クイズを解くことで、メインスレッドを ブロックさせないために必要な規則や ドキュメントのありかを知ること ◦ 頭の中にインデックスを貼る 18
トークのゴール (熟練者) • 理解度の確認ができること 19
20 ソースコードの共有
ソースコードの共有 • コードのみ(答え無し・解説無し) ◦ https://tokizuoh.hatenablog.com/iosdc-japan-2024-quiz 21
22 クイズのルール
クイズのルール (1/2) • Xcode 15.4 ◦ Swift 5.10 23
クイズのルール (2/2) // ✅: 通る → MainActor上、つまりメインスレッドで実行される* // ❌: 通らない
→ MainActor上で実行されない MainActor.assertIsolated() 24 *: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0316-global-actors.md
25 練習
import UIKit class PracticeViewController: UIViewController { override func viewDidLoad() {
super.viewDidLoad() MainActor.assertIsolated() // ✅ or ❌ ? } } 26
27 答え
import UIKit class PracticeViewController: UIViewController { override func viewDidLoad() {
super.viewDidLoad() MainActor.assertIsolated() // ✅ } } 28
29 解説
練習: 解説 (1/3) • UIViewController には @MainActor が付いている 30 https://developer.apple.com/documentation/uikit/uiviewcontroller
練習: 解説 (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 の隔離を 推論される**
練習: 解説 (3/3) • よって、viewDidLoad() は MainActor 上で実行 される ◦
→ メインスレッドで実行される 32
import UIKit class PracticeViewController: UIViewController { override func viewDidLoad() {
super.viewDidLoad() MainActor.assertIsolated() // ✅ } } 33
34 正解の方
35 練習おわり
36 全5問
37 参ります
38 問1
import UIKit class Q1ViewController: UIViewController { override func viewDidLoad() {
super.viewDidLoad() Task.detached { MainActor.assertIsolated() // ✅ or ❌ ? } } } 39
40 答え
41 import UIKit class Q1ViewController: UIViewController { override func viewDidLoad()
{ super.viewDidLoad() Task.detached { MainActor.assertIsolated() // ❌ } } }
42 解説
問1: 解説 • Detached Task は呼び出し側の Actor の 一部ではない、非同期に実行できる作業単位* ◦
→ MainActor とは別で実行されるため、 メインスレッドで実行されない 43 *: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/#Unstructured-Concurrency
44 import UIKit class Q1ViewController: UIViewController { override func viewDidLoad()
{ super.viewDidLoad() Task.detached { MainActor.assertIsolated() // ❌ } } }
45 正解の方🙋
import UIKit // 以後省略 class Q1ViewController: UIViewController { override func
viewDidLoad() { super.viewDidLoad() Task.detached { MainActor.assertIsolated() // ❌ } } } 46
47 問2
func foo() async { MainActor.assertIsolated() // ✅ or ❌ ?
} class Q2ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { @MainActor in await foo() } } } 48
49 答え
func foo() async { MainActor.assertIsolated() // ❌ } class Q2ViewController:
UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { @MainActor in await foo() } } } 50
51 解説
問2: 解説 (1/3) • in の前に @MainActor を付けると、 クロージャの処理が Main
Actor で実行される* 52 https://github.com/swiftlang/swift-evolution/blob/main/proposals/0316-global-actors.md#closures
問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
@MainActor func foo() async { MainActor.assertIsolated() // ✅ } •
関数に @MainActor を付けると通る 問2: 解説 (3/3) 54
func foo() async { MainActor.assertIsolated() // ❌ } class Q2ViewController:
UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { @MainActor in await foo() } } } 55
56 正解の方
57 問3
@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
59 答え
@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
61 解説
問3: 解説 (1/3) • Global Actor が付与された wrappedValue を持つ プロパティラッパーが付与されたプロパティを持つ
構造体やクラスはそのプロパティラッパーから Actor の隔離が推論される* 62 *: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0316-global-actors.md#global-actor-inference
問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() /* ✅ */ } }
問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
@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
66 正解の方🙋
67 問4
@MainActor class Animal { deinit { MainActor.assertIsolated() // ✅ or
❌ ? } } class Q4ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { await Animal() } } } 68
69 答え
@MainActor class Animal { deinit { MainActor.assertIsolated() // ❌ }
} class Q4ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { await Animal() } } } 70
71 解説
問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
@MainActor class Animal { deinit { MainActor.assertIsolated() // ❌ }
} class Q4ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() Task.detached { await Animal() } } } 73
74 正解の方
75 問5
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
77 答え
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
79 解説
問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
問5: 解説 (2/3) 81 https://github.com/swiftlang/swift-evolution/blob/main/proposals/0316-global-actors.md#global-actor-inference
• 問5のコードをよく見ると、await が無いので 同期メソッドを呼んでいるだけ class Q5ViewController: UIViewController { override func
viewDidLoad() { super.viewDidLoad() Task.detached { Y().f() } } } 問5: 解説 (3/3) 82
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
84 正解の方🙋
85 全5問 ありがとうございました!
クイズ まとめ • 問1: SE-0304 Detached Task • 問2: SE-0338
Actor 隔離されていない非同期関数 • 問3: SE-0401 プロパティラッパーからの推論 • 問4: SE-0316 Global Actorの規則 • 問5: SE-0316 Global Actorの規則 86
87 何問解けましたか?
88 全問正解の方 🙋
89 拍手👏
90 メインスレッドを 適切に 使っていきましょう!