Slide 1

Slide 1 text

Swift Concurrency 入門 2022 年 2 月 22 日 集まれ Swift 好き! Swift 愛好会 vol.65 @ オンライン

Slide 2

Slide 2 text

Who am I ● Name ● 佐藤タケシ ( さとうたけし ) ● Company ● Merpay, Inc.(2019/01 ~) ● Role ● Software Engineer (iOS) ● Account ● Twitter: @hatakenokakashi ● Facebook: 佐藤剛士 ● GitHub: SatoTakeshiX

Slide 3

Slide 3 text

● 一冊で Swift Concurrency の概 要を理解ことを目指して書いて います ● まだ執筆中 ● Swift Concurrency は新しい概 念が盛りだくさん ● 覚えればあなたの開発がより安 全に、簡単に実装できるように なる 「Swift Concurrency入門」で技術 書典で販売予定

Slide 4

Slide 4 text

Swift Concurrency ● Swift 5.5 から導入 ● Xcode 13 から利用可能 ● 非同期処理が簡潔、安全に ● async/await が Swift にやってき た!

Slide 5

Slide 5 text

コールバックによる 非同期処理の 欠点

Slide 6

Slide 6 text

ネストが深くなる

Slide 7

Slide 7 text

コールバックの呼び忘れ

Slide 8

Slide 8 text

コールバックの呼び忘れ コールバックを呼ばないで returnしている

Slide 9

Slide 9 text

呼び出し元で不具合発生 コールバックが呼ばれないので、 エラーが起きているのに、 ローディングビューがずっと出しっぱな しになるかもしれない

Slide 10

Slide 10 text

async/awaitで解決

Slide 11

Slide 11 text

async/awaitで非同期処理 ● async で非同期関数を定義

Slide 12

Slide 12 text

async/awaitで非同期処理 ● 呼び出し元

Slide 13

Slide 13 text

非同期関数 ● async がつけられた関数・メソッド ● プログラムを待機、再開させる ● 通常のコードと同じように記述ができる ○ 呼び出し元で、変数の代入や try-catch によるエラーハンドリング ○ 戻り値を定義できるのでコールバックのよう呼び忘れがない。コンパイルの チェックが入る ● スレッドは CPU のコア数分用意される ○ システムが管理し、開発者が直接触ることはない

Slide 14

Slide 14 text

awaitでプログラムが待機する

Slide 15

Slide 15 text

awaitでプログラムが待機する

Slide 16

Slide 16 text

awaitでプログラムが待機する

Slide 17

Slide 17 text

順次実行の実装の違い

Slide 18

Slide 18 text

順次実行をする by 同期関数 ネストが深くなる

Slide 19

Slide 19 text

順次実行をする by 非同期関数

Slide 20

Slide 20 text

並列実行の実装の違い

Slide 21

Slide 21 text

並列実行をする by 同期関数

Slide 22

Slide 22 text

並列実行をする by 非同期関数

Slide 23

Slide 23 text

データ競合を防ぐ新しい型 Actor

Slide 24

Slide 24 text

データ競合(Data Race)とは? ● 複数のスレッドがデータに同時にアクセスしたためにデータが不正になる状況 ○ スレッドの少なくとも一つが値を書き込む 値 スレッド 1 スレッド 2

Slide 25

Slide 25 text

データ競合(Data Race)

Slide 26

Slide 26 text

データ競合(Data Race) playgroundで実行すると 110,110が出力される場合がある

Slide 27

Slide 27 text

actorの特徴 ● 新しい型の種類 ● 参照型 ● インスタンスに外からアクセスは同時にひとつのみに限定される ○ Actor 隔離( Actor isolated) と呼ぶ ○ data race を防ぐ ● 外からアクセスする場合は await が必要 ● イニシャライザー、プロパティ、メソッド定義、プロトコル適応など class, struct, enum を同じ特徴をもつ

Slide 28

Slide 28 text

actorでデータ競合を防ぐ

Slide 29

Slide 29 text

actorでデータ競合を防ぐ 必ず100, 110が順不同で出力される -> データ競合がなくなる

Slide 30

Slide 30 text

actorで競合状態(Race Condition)は防げない ● 競合状態 (Race Condition) マルチスレッドにおける典型的な不具合の一つ ● プログラミングの実行結果が各スレッドの実行順に依存する状態 ○ 同じ入力を与えても異なるデータを出力する状態

Slide 31

Slide 31 text

actorで競合状態(Race Condition)は防げない

Slide 32

Slide 32 text

actorで競合状態(Race Condition)は防げない

Slide 33

Slide 33 text

actorで競合状態(Race Condition)は防げない 1. ③、④が同時に呼び出す 2. キャッシュはまだない 3. ③が①に到達。待機 4. ④が①に到達。待機 5. ③が再開。👾を返す。 6. ③が②に到達。 return する 7. ④が再開。🎃を返す。 8. ④が②に到達。 return する 9. ③、④同じ URL なのに違うコ ンテンツが返される=>競合 状態

Slide 34

Slide 34 text

競合状態(Race Condition)の修正 ● await の後でプロパティの チェックする ● Protect mutable state with Swift actorsに Task を使った例もあり

Slide 35

Slide 35 text

MainActor ● メインスレッドで実行される特別な Actor ● Global Actor の一種 ○ むしろメインスレッドのために Global Actor が提案された ○ 共有の actor インスタンスを通して Actor 分離がされる ● @MainActor で型全部、プロパティのみ、メソッドのみなど適応できる

Slide 36

Slide 36 text

MainActorの適応方法 ● MainActor の適応手順をコードで示す

Slide 37

Slide 37 text

Task

Slide 38

Slide 38 text

Task ● 並行処理の基本単位。すべての非同期関数は Task を通して実行される ● 各 Task を Task Tree とよばれる親子関係を構築する ● Task Tree のおかげでキャンセルや優先度をシンプルにハンドリング可能 ○ 従来の方法だとあるスレッドの処理がキャンセルされた場合に他のスレッドの処 理に伝播させるのが難しかった

Slide 39

Slide 39 text

Task Tree ● ①一番下位のタ スクがすべて終わ ると上位のタスク が始まる ● ②子タスクが終わ ると親タスクに伝 播 ● ③親タスクが実行

Slide 40

Slide 40 text

Task Tree ● ①がエラー -> 兄弟 タスクはキャンセ ル② ● 上位のタスク③が キャンセル。兄弟 タスク④もキャン セル ● ④の下位タスク⑤ もキャンセル ● ⑥親タスクに①の エラー伝播

Slide 41

Slide 41 text

async letバインディングとTask Tree

Slide 42

Slide 42 text

async letバインディングとTask Tree 親タスク 子タスク 子タスク

Slide 43

Slide 43 text

同期関数から非同期関数を呼ぶ方法 Task イニシャライザー ● 非同期コンテキストを提供 ● 同期関数内から非同期関数を呼べる ● コールバックはすぐに呼ばれる ● 返り値の Task インスタンスからマニュアルで キャンセルできる ● 親タスクの優先度や actor, タスクローカル値 を引き継ぐ Task.detached ● 非同期コンテキストを提供 ● 同期関数内から非同期関数を呼べる ● コールバックはすぐに呼ばれる ● 返り値の Task インスタンスからマニュアルで キャンセルできる ● 親タスクの優先度や actor, タスクローカル値 を引き継がない

Slide 44

Slide 44 text

TaskイニシャライザーとTask.detached ● Task.detached は MainActor を引き継 がない -> ログ送信な どメインスレッドで不 要な処理に向く ● Task は MainActor を 引き継ぐ -> メインス レッドで実行

Slide 45

Slide 45 text

マニュアルキャンセル ● Task のイニシャライザーを保持しておけば、キャ ンセルが必要なときに cancel メソッドでキャンセル できる

Slide 46

Slide 46 text

キャンセルのハンドリング ● checkCancellation: Task が キャンセルされている場合に CancellationError を返す ○ キャンセルを呼び出し元 に伝える ● isCancelled: キャンセルかど うか Bool 値で判断 ○ キャンセル時追加処理が 可能 独自の追加処理

Slide 47

Slide 47 text

iOS 13, 14で非同期関数を使う

Slide 48

Slide 48 text

XcodeのリリースとSwift Concurrency対応状況 ● Xcode 13 ○ 2021 年 9 月 21 日リリース ○ Swift Concurrency 機能は iOS 15 のみ ● Xcode 13.2 ○ 2021 年 12 月 13 日リリース ○ Swift Concurrency 機能が iOS 13, 14 にもバックポートされた ○ ただし Swift パッケージがビルドできない不具合が見つかる ● Xcode 13.2.1 ○ 2021 年 12 月 17 日リリース ○ Swift パッケージの不具合を修正

Slide 49

Slide 49 text

iOS 13, 14で非同期関数を使う ● 言語機能としての Swift Concurrency 機能は使える ● Foundation での非同期関数は iOS 15 以上 ○ URLSession の data メソッドなど ● withCheckedThrowingContinuation と withCheckedContinuation で既存の コールバック関数を非同期関数にラップができる ● アプリサポートバージョンが iOS 15 が下限になるまではラップした関数を呼び出 す

Slide 50

Slide 50 text

withCheckedContinuation ● エラーをスローするしないコールバック関数を非同期関数にラップする ● クロージャー内でラップする関数を呼ぶ ● ラップする関数のコールバック内で continuation は確実に一回 resume メソッドを 呼び出す ○ 呼び出さないのはだめ ○ 2回以上呼び出すのもだめ

Slide 51

Slide 51 text

withCheckedContinuation ●

Slide 52

Slide 52 text

withCheckedThrowingContinuation ● エラーをスローするコールバック関数を非同期関数にラップする ● クロージャー内でラップする関数を呼ぶ ● ラップする関数のコールバック内で continuation は確実に一回 resume メソッドを 呼び出す ● resume(returning:) メソッド ○ ラップした関数の戻り値を引数にする ● resume(with: ) ○ Result 型で返す ● continuation.resume(throwing:) ○ エラー型で返す

Slide 53

Slide 53 text

withCheckedThrowingContinuation ●

Slide 54

Slide 54 text

まとめ

Slide 55

Slide 55 text

まとめ ● async/await で非同期処理を同期関数のように記述できるようになった ● それによって読みやすさが向上し、コールバックの呼び忘れなどのミスを防 げるようになった ● マルチスレッドでよくある不具合、データ競合を防ぐ新しい型、 actor が登場 した ○ ただし、競合状態はまだ防げない ● 非同期処理は Task という単位で行われる ● iOS 13, 14 でも Swift Concurrency は使える

Slide 56

Slide 56 text

参考資料 ● Race condition vs. Data Race: the differences explained ● データ競合 (data race) と競合状態 (race condition) を混同しない ● Protect mutable state with Swift actors ● https://github.com/apple/swift-evolution/blob/main/proposals/0316-glo bal-actors.md ● Explore structured concurrency in Swift ● Meet async/await in Swift