【Swift Concurrency】@MainActorと@globalActorの違いと使い方!
この記事からわかること
- Swift Concurrencyとは?
- @MainActorと@globalActorの違いと使い方
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Xcode:16.0
- iOS:18.0
- Swift:5.9
- macOS:Sonoma 14.6.1
Swift Concurrency
で導入されたActor
は異なるスレッド間からの同時アクセスを制御しデータの競合が発生して不整合が起きないようにするためののものでした。
複数のスレッドからのアクセスを制御できるActor
を学んでいくと@MainActor
属性と@globalActor
属性が出てきます。今回はこれらについてまとめていきます。
Global Actor(グローバルアクター)
「Global Actor(グローバルアクター)」とは特定のスレッドで実行されることを保証することができるカスタム可能なActorで@globalActor
属性を使用することで独自のGlobal Actorを定義することができます。
@globalActor
struct BackgroundActor {
// シングルトンのアクターを保持する
static let shared = BackgroundActor()
}
actor BackgroundActor { }
例えば上記のように定義したBackgroundActor
は関数やクラスなどの前に付与することで実行するスレッドをBackgroundActorのスレッドに強制させることができます。
@BackgroundActor
func performBackgroundTask() {
// この関数は常にBackgroundActorのスレッドで実行される
print("Background task running")
}
@MainActor属性
@globalActor
final public actor MainActor : GlobalActor {
public static let shared: MainActor
}
@MainActor
属性はあらかじめ定義されているGlobal Actorです。名前の通り@MainActor
属性を付与した関数などをメインスレッドでの実行を強制することができるGlobal Actorです。Swiftの世界でも実際に使用されていてSwift UIではViewプロトコルのbody
プロパティやUIKitではUIViewController
などに付与されています。
public protocol View {
associatedtype Body : View
@ViewBuilder @MainActor var body: Self.Body { get }
}
@MainActor
class UIViewController : UIResponder
使い方
@MainActor
属性は関数やクラスに対して付与して使用します。例えばグローバル関数として定義されたshowPopUpWindow
があるとします。このメソッドはUIを更新するような処理の場合、メインスレッドからの操作が必要不可欠になります。このような場合に@MainActor
属性を付与するとメインスレッドで実行されることが保証されるようになります。
@MainActor
func showPopUpWindow() {
// メインスレッドでの実行が保証される
// UI更新処理
}
@MainActor
属性を付与した関数を呼び出す場合はメインスレッドから非同期的に呼び出す必要があり、非同期的に呼び出さない場合などは以下のエラーを吐きます。
- Call to main actor-isolated global function 'showPopUpWindow()' in a synchronous nonisolated context
そのためDispatchQueue.main.async
やTask { await }
を活用して明示的に非同期かつメインスレッドで呼び出すようにします。
DispatchQueue.main.async {
showPopUpWindow()
}
Task {
await showPopUpWindow()
}
呼び出す関数に@MainActor
が付与されていればその中では同期的に呼び出すことができますが結果その関数を呼び出すときには同じになります。
@MainActor
func callShowPopUpWindow() {
showPopUpWindow()
}
クラスに付与する
@MainActor
は関数だけでなくクラス自体にも付与することができます。@MainActor
を付与したクラスはインスタンス化する際もメインスレッドから非同期的に呼び出す必要があります。
@MainActor
class TestClass {
func sampleFunction() { }
}
nonisolated
キーワードを付与することで特定のメソッドだけisolate
(隔離)しないようにすることもできます。
@MainActor
class TestClass {
nonisolated func sampleFunction() { }
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。