参照
提案概要
- 「Global actor isolated types(GlobalActorに隔離された型)」に関する、いくつかのConcurrencyルールの変更
- GlobalActorに隔離された型とは?
- @MainActorが付与された型などが一例
- GlobalActorに隔離された型とは?
- 提案内容は5つ
- GlobalActorに隔離された型のストアドプロパティーの型が
Sendableである場合に-
nonisolated(unsafe)を付与せずとも、nonisoaltedな値として宣言することを許可する - その値が宣言されたmodule内部であれば、
nonisolatedな値として扱う
-
- GlobalActorに隔離された関数やクロージャを暗黙的に
Sendableな関数やクロージャとして扱う - GlobalActorに隔離されたクロージャが、
Non-Sendableな値をキャプチャすることを許可する - Actor隔離されておらず、Sendableでもないclassのサブクラスが、GlobalActorに隔離されることを許可する。しかし、そのサブクラスはNon-Sendableでなければならない
- GlobalActorに隔離された型のストアドプロパティーの型が
GlobalActorに隔離された型のストアドプロパティーの型が Sendable である場合に nonisolated(unsafe) を付与せずとも、nonisoaltedな値として宣言することを許可する
&
GlobalActorに隔離された型のストアドプロパティーの型が Sendable である場合にその値が宣言されたmodule内部であれば、 nonisolated な値として扱う
概要
@MainActor
struct S {
var x: Int = 0 // okay ('nonisolated' is inferred within the module)
}
extension S: Equatable {
static nonisolated func ==(lhs: S, rhs: S) -> Bool {
return lhs.x == rhs.x // okay
}
}なぜ必要か
- 既存のSwiftバージョンでは、上記のxはデータ競合の恐れがないにも関わらず、nonisolated(unsafe)をつける必要があり、不要な手間である
GlobalActorに隔離された関数やクロージャを暗黙的に Sendable な関数やクロージャとして扱う
概要
func test(globallyIsolated: @escaping @MainActor () -> Void) {
Task {
// error: capture of 'globallyIsolated' with non-sendable type '@MainActor () -> Void' in a `@Sendable` closure
await globallyIsolated()
}
}なぜ必要か
- 上記
globallyIsolatedclosureは@MainActorで実行されるのでSendableなクロージャとして扱って差し支えないが、現行のSwiftバージョンではエラーになる
GlobalActorに隔離されたクロージャが、 Non-Sendable な値をキャプチャすることを許可する
概要
class NonSendable {}
func test() {
let ns = NonSendable()
let closure { @MainActor in
print(ns) // 既存ではエラー
}
Task {
await closure() // okay
}
}なぜ必要か
- GlobalActorに隔離されたクロージャの利便性(Usability)を上げるため
どのように使うか
- GlobalActorに隔離されたクロージャによってキャプチャされたインスタンスは、キャプチャ前のインスタンスとは別になることに注意
- キャプチャした際に共通して起こる挙動なので特別な話ではない
備考
- RegionBasedIsolationが適用されているので、nsが同一インスタンスではないことを実際に動かして検証できない
Actor隔離されておらず、Sendableでもないclassのサブクラスが、GlobalActorに隔離されることを許可する。しかし、そのサブクラスはNon-Sendableでなければならない
概要
Before
class NotSendable {}
@MainActor
class Subclass: NotSendable {} // error: main actor-isolated class 'Subclass' has different actor isolation from nonisolated superclass 'NotSendable'After
- サブクラスにGlobalActorの付与できるようにするが、GlobalActorに隔離された型を暗黙的にSendableとして扱うのはやめるよ
class NonSendable {
func test() {}
}
@MainActor
class IsolatedSubclass: NonSendable {
func trySendableCapture() {
Task.detached {
self.test() // error: Capture of 'self' with non-sendable type 'IsolatedSubclass' in a `@Sendable` closure
}
}
}なぜ必要か
- Concurrency周りの利便性を上げるため
- サブクラスしたらGlobalActor付与でないのでは、基底クラス自体がGlobalActorに準拠している必要がでてくる
感想
- 最後のは特にややこしいルール…
技術メモ