【Swift Concurrency】@MainActorと@globalActorの違いと使い方!

【Swift Concurrency】@MainActorと@globalActorの違いと使い方!

この記事からわかること

  • Swift Concurrencyとは?
  • @MainActor@globalActor違い使い方

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

Swift Concurrencyで導入されたActor異なるスレッド間からの同時アクセスを制御しデータの競合が発生して不整合が起きないようにするためののものでした。

複数のスレッドからのアクセスを制御できるActorを学んでいくと@MainActor属性と@globalActor属性が出てきます。今回はこれらについてまとめていきます。

Global Actor(グローバルアクター)

公式リファレンス:0316 Global Actors

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属性

公式リファレンス:@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属性を付与した関数を呼び出す場合はメインスレッドから非同期的に呼び出す必要があり、非同期的に呼び出さない場合などは以下のエラーを吐きます。

そのためDispatchQueue.main.asyncTask { 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() { }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index