Skip to content

Commit 44a725e

Browse files
authored
Merge pull request #1439 from future-architect/feature
Swift6
2 parents 67f1a2a + 3672ef4 commit 44a725e

File tree

4 files changed

+182
-0
lines changed

4 files changed

+182
-0
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
---
2+
title: "Swift6移行に向けて、Isolation domainとSendableを理解する"
3+
date: 2024/12/08 00:00:00
4+
postid: a
5+
tag:
6+
- iOS
7+
- Swift
8+
- Swift6
9+
category:
10+
- Programming
11+
thumbnail: /images/20241208a/thumbnail.png
12+
author: 橋本竜我
13+
lede: "SwiftZoomin#20の内容から、Swift6移行に向けて理解が必要なSwift Concurrencyの重要な概念について簡単にまとめました。"
14+
---
15+
<img src="/images/20241208a/image.png" alt="" width="1200" height="416" loading="lazy">
16+
17+
<div class="note info" style="background: #e5f8e2; padding:16px; margin:24px 12px; border-radius:8px;"><span class="fa fa-fw fa-check-circle"></span>
18+
19+
本記事は、[Swift Advent Calendar 2024](https://qiita.com/advent-calendar/2024/swift)の8日目です。
20+
7日目は、[@hinakko](https://qiita.com/hinakko)さんの[Split Viewを考慮したSize Classを使用したiPad対応](https://qiita.com/hinakko/items/ac65d149e3141a8bc7da)です。
21+
22+
</div>
23+
24+
HealthCare Innovation Group(HIG)[^1]の橋本です。
25+
26+
先日参加したSwiftZoomin#20の内容から、Swift6移行に向けて理解が必要なSwift Concurrencyの重要な概念について簡単にまとめました。
27+
28+
SwiftZoomin#20の動画は、次のリンク先からYoutube上で視聴可能です。
29+
- [感覚的に理解するConcurrency: Swift 6はIsolationとSendableを用いてどのようにデータ競合を防止するか](https://youtu.be/AUcn2y2jjNs?si=_fyNjme2hDA236sl)
30+
31+
# Swift6移行に向けて、重要な概念3つ
32+
33+
以下3つの概念についてまとめいきます。
34+
35+
- Isolation domain
36+
- Isolation boundary
37+
- Sendable
38+
39+
## Isolation domain
40+
41+
`隔離=Isolation`する領域のことを`Isolation domain`と呼びます。
42+
`Isolation domain`の重要な特性は、**一つのIsolation domainのなかでは同時に一つの処理しか実行されない**ことです。(=一つのIsolation domainの中で並行実行されることはないことと同義)
43+
44+
参考
45+
46+
- [Isolation Domains | Migrating to Swift 6](https://www.swift.org/migration/documentation/swift-6-concurrency-migration-guide/dataracesafety/#Isolation-Domains)
47+
48+
### actor
49+
50+
`actor`というのは、一つの領域で隔離(Isolation)することでデータ競合を防いでいます。
51+
52+
最も馴染み深いのは、`MainActor``Isolation domain`であり、`actor`のインスタンスに紐づいたIsolation domainも存在します。
53+
54+
`MainActor``Isolation domain``Main Actor`で保護された領域を表します。
55+
`Actor`の場合は、`Actor`のインスタンスごとに`Isolation domain`をもち、同じ`actor`でもインスタンスが異なれば、それぞれ異なる`Isolation domain`を持ちます。
56+
57+
```swift
58+
actor Counter {...}
59+
60+
let a = Counter() // aのIsolation domain、aとbは別のIsolation domainである。
61+
let b = Counter() // bのIsolation domain、aとbは別のIsolation domainである。
62+
63+
```
64+
65+
参考
66+
67+
- [Actor | Apple Developer Documentation](https://developer.apple.com/documentation/swift/actor)
68+
69+
## Isolation boundary
70+
71+
`Isolation domain`が複数存在しているときのそれぞれの`Isolation domain`の間の境界のことを`Isolation boundary`と呼びます。
72+
73+
アプリにおいては、それぞれの`Isolation domain`間で`Isolation boundary`を超えて情報のやり取りをしないといけない場面が多々あります。
74+
75+
この`Isolation boundary`を超えて値を受け渡すために、次の`Sendable`が必要になってきます。
76+
77+
## Sendable
78+
79+
**Sendable 並行にアクセスされても安全な型だけが準拠できるプロトコル**
80+
`Sendable`の特徴は、`Sendable`に準拠した型の値だけが`Isolation boudnary`を超えられるようになります。
81+
つまり、`Sendable`を使うことで実行時にデータ競合が起こらないことをコンパイル時にチェックできるように、型の問題とすることで実現しています。
82+
83+
### non-Sendable
84+
85+
`Sendable`に対して、`non-Sendable`な値は、`Isolation boundary`を超えることができません。
86+
87+
88+
- [Sendable | Apple Developer Documentation](https://developer.apple.com/documentation/swift/sendable)
89+
- [Sendable Types | Migrating to Swift 6](https://www.swift.org/migration/documentation/swift-6-concurrency-migration-guide/dataracesafety/#Sendable-Types)
90+
- [Sendable Types | The Swift Programming Language](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/#Sendable-Types)
91+
92+
## Sendableを体感する
93+
94+
<img src="/images/20241208a/Sendable.png" alt="" width="1031" height="685" loading="lazy">
95+
96+
上記の`Box`クラスをSendableに準拠させて、並行にアクセス可能にさせてみる。
97+
98+
```swift
99+
final class Box: Sendable {
100+
var value: Int = 0 // ここでエラー(Stored property 'value' of 'Sendable'-conforming class 'Box' is mutable)
101+
}
102+
```
103+
104+
そうすると、次のように`Sendable`に準拠すること自体がエラーであることがわかります。
105+
`Box`型のプロパティ`Value`が可変状態のため、並行にアクセスすると危険である旨のメッセージが出てきます。
106+
107+
```txt
108+
Stored property 'value' of 'Sendable'-conforming class 'Box' is mutable
109+
```
110+
111+
ここで、`var`ではなく、`let`だったらどうなるか考えてみます。
112+
113+
```swift
114+
final class Box: Sendable {
115+
let value: Int = 0
116+
}
117+
```
118+
119+
`let`にすることで、`Box`クラスのエラーは解消されます。
120+
121+
`value`プロパティが定数になるため、`run()`メソッド内で`box.balue = -1`で書き込みをしていた箇所がコンパイルエラーになります。
122+
123+
この部分を読み込むだけの`print(box.value)`とすれば、データ競合を起こさない安全なコードとすることができます。データ競合は、並行にアクセスする少なくとも1つで書き換えが起こっているときに生じるものであるため、今回のように読み込むだけであれば、データ競合の危険性はありません。
124+
125+
```swift
126+
func run() {
127+
let box: Box = .init()
128+
129+
Task {
130+
// box.value = -1 // Cannot assign to property: 'value' is a 'let' constant
131+
print(box.value)
132+
}
133+
134+
Task {
135+
print(box.value)
136+
}
137+
}
138+
```
139+
140+
## Isolation boundaryを体感する
141+
142+
`Sendable`に準拠していない`non-Sendable``Box`クラスが`Isolation boundary`を超えるとコンパイルエラーが出る例を示します。
143+
144+
```swift
145+
final class Box {
146+
var value: Int = 0
147+
}
148+
149+
150+
actor A {
151+
var box: Box?
152+
func setBox(_ box: Box) {
153+
self.box = box
154+
}
155+
}
156+
157+
158+
func run() async {
159+
let box: Box = .init()
160+
let a: A = .init() // actor A のインスタンスa
161+
162+
await a.setBox(box) // actor A のインスタンスaにboxを渡す ⇐ boxがIsolation bounadaryを超えて、actor AのインスタンスaのIsolation domainに入る。
163+
// ここで次のエラーメッセージが出る。
164+
// Sending 'box' risks causing data races
165+
166+
print(box.value)
167+
}
168+
```
169+
170+
`Sendable`に準拠していない`Box`クラスの`box`インスタンスが`Isolaton boudary`を超えたため、このエラーがでてきます。
171+
172+
このように、`Isolaton boudary`が存在していることを体験することができました。
173+
174+
# おわりに
175+
176+
SwiftZoomin#20[感覚的に理解するConcurrency: Swift 6はIsolationとSendableを用いてどのようにデータ競合を防止するか](https://youtu.be/AUcn2y2jjNs?si=_fyNjme2hDA236sl))動画前半の内容について私なりにまとめさせていただきました。([@koher](https://qiita.com/koher)さん、大変わかりやすく説明いただきありがとうございました。)
177+
178+
Swift6への移行は、コンパイル時にデータ競合が起こる可能性のある箇所を事前につぶすことができるので、アプリの品質向上に直結するため、ビジネス的にもとても有用なものだと思っております。
179+
180+
本記事がSwift6移行に向けて、少しでも参考になれば幸いです。
181+
182+
[^1]: 医療・ヘルスケア分野での案件や新規ビジネス創出を担う、2020年に誕生した事業部です。設立エピソードは[未来報](https://note.future.co.jp/n/n8b57d4bf4604)の記事をご覧ください。
32.1 KB
Loading

source/images/20241208a/image.png

222 KB
Loading
16.5 KB
Loading

0 commit comments

Comments
 (0)