Actor — データ分離と並行処理とは

著者: IT Sectr 公開日: 2026-06-18 読了時間: 11 分

ActorはSwift 5.5+で導入された型であり、言語レベルでデータ競合の問題を解決します。手動ロックやDispatchQueueキューとは異なり、actorは自動的にその状態を隔離し、アクセスを同期します。つまり、2つのスレッドが同時にactor型の同じプロパティを変更することはできず、開発者の追加作業なしに競合状態を排除できます。このメカニズムはactor隔離の概念に基づいており、コンパイラがactorのプロパティとメソッドへのアクセスを追跡します。Apple、2025によると、actorモデルはSwiftの並行処理システムの基本的な部分です。

重要なポイント

  • Actorはコンパイラレベルで自動的に状態をデータ競合から隔離する型です
  • 隔離により、一度に1つのスレッドだけがactorインスタンスにアクセスすることが保証されます
  • 呼び出しはasync/awaitメカニズムを介して非同期に実行されます
  • Nonisolatedを使用すると、個々のactorメンバーの隔離を選択的に無効にできます
  • Reentrancyによりactorへの再入が可能になり、デッドロックを防ぎます

SwiftにおけるActorとは

Actorは参照型であり、クラスに似ていますが、データ競合に対する自動保護機能を備えています。Swift 5.5でasync/awaitやTaskとともに並行処理システムの一部として導入されました。Actorは、明示的な同期なしに異なるスレッドからその可変状態が同時に読み書きされることが決してないことを保証します。

Actorの宣言構文

Actorを宣言するには、actorキーワードを使用し、その後にメンバーを持つ中括弧を続けます。構文的にはactorはクラスに似ていますが、その動作は根本的に異なります。

swift
actor BankAccount {
    private var balance: Double
    
    init(initialBalance: Double) {
        self.balance = initialBalance
    }
    
    func deposit(amount: Double) {
        balance += amount
    }
    
    func getBalance() -> Double {
        return balance
    }
}

コンパイラはすべてのactorのプロパティとメソッドを自動的に隔離し、actorコンテキスト内でのみアクセス可能にします。actorの外部からbalanceにアクセスしようとすると、呼び出しがasyncとしてマークされていない場合、コンパイラはエラーを生成します。

Actorでのデータ隔離の仕組み

データ隔離はactorの重要な概念です。Actorはactor実行メカニズムを通じて、その状態への相互排他的アクセスを保証します。各actorには独自の実行基盤があり、隔離されたメンバーへのすべてのアクセスを順次処理します。

隔離の仕組み

actor外部のコードがそのメソッドを呼び出すと、呼び出しはactor実行基盤のキューに配置されます。Actorは一度に1つのタスクのみを実行し、競合がないことを保証します。2つのスレッドが同時にdepositを呼び出すと、2番目の呼び出しは最初の呼び出しが完了するまで待機します。

swift
let account = BankAccount(initialBalance: 1000.0)
// 非同期呼び出し — actorの外部から必要
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()

actorメソッドの呼び出しにはすべてawaitが必要です。actorが別のタスクでビジー状態である可能性があるためです。これはバグではなく、競合を防ぐための意図的な設計です。Swiftは暗黙的にactorプロパティのゲッターとセッターを非同期にするため、プロパティの読み取りにもawaitが必要です。

Actorとクラスの主な違い

Actorとクラスはどちらも参照型ですが、マルチスレッド環境での動作は大きく異なります。クラスは競合に対する自動保護を一切提供しませんが、actorは型システムを通じてコンパイラレベルでそれを組み込みます。

特性Actorクラス
競合保護自動、コンパイラレベル手動同期が必要
プロパティアクセス外部からはawait経由のみ直接、同期なし
継承他のactorからのみ標準のクラスベース
プロトコル準拠プロトコルに準拠可能標準
パフォーマンス隔離による低オーバーヘッド同期なしで高速

Actorの継承

Actorは別のactorからのみ継承でき、クラスから継承することはできません。これは意図的なもので、クラスにはactor隔離メカニズムがなく、型を混在させると安全性の保証が損なわれるためです。

swift
actor SavingsAccount: BankAccount {
    func applyInterest(rate: Double) {
        let interest = balance * rate
        balance += interest
    }
}

Actorメソッドの非同期呼び出し

非同期呼び出しは、外部コードからactorと対話するためのメカニズムです。Actorはその状態を隔離するため、外部からそのメンバーにアクセスするにはawaitが必要です。これにより、呼び出し元のコードがスレッドをブロックせず、actorが他のリクエストを処理できることが保証されます。

グローバルActorとローカルActor

宣言されたactor型に加えて、Swiftはグローバルactor — @MainActor属性をサポートしています。これはクラス、プロパティ、またはメソッドをメインスレッドで実行可能としてマークします。これはiOSアプリケーションのUIコードに特に便利です。

swift
@MainActor
class ViewModel: ObservableObject {
    @Published var title: String = ""
    
    func updateTitle() {
        // このコードはメインスレッドで実行されることが保証されています
        title = "New Title"
    }
}

@MainActorを使用すると、手動でDispatchQueue.main.asyncを呼び出す必要がなくなり、コードがよりクリーンで安全になります。コンパイラはメインスレッドへの切り替えが正しく行われることを確認します。

NonisolatedとActor-Isolatedコンテキスト

Nonisolatedは、actorのメソッドまたは計算プロパティを非隔離としてマークできるキーワードです。これらのメンバーはactor隔離状態にアクセスできませんが、actorの外部からawaitなしで呼び出すことができます。

Nonisolated関数

Nonisolatedメソッドは、actorの可変状態に依存しない計算に役立ちます。たとえば、formatBalanceメソッドはbalanceに直接アクセスせず、渡された値をフォーマットするだけです。このようなメソッドはnonisolatedにしても安全です。

swift
actor BankAccount {
    private var balance: Double = 0
    
    nonisolated func formatBalance(amount: Double) -> String {
        return "$\(amount)"
    }
}

Nonisolatedメンバーは同期的に実行され、awaitを必要としません。ただし、actor隔離プロパティを直接読み取ることはできません。Nonisolatedメソッドがactorから値を受け取る必要がある場合は、パラメータとして渡す必要があります。

Reentrancyと再入

Reentrancyは、非同期呼び出しの待機中にactorへの再入を許可するメカニズムです。Reentrancyがないと、あるメソッドが別のメソッドを待機し、そのメソッドが最初のメソッドを待機する場合、actorは永久にデッドロックする可能性があります。

Reentrancyの仕組み

Actor内のコードがawaitを実行すると、actorは現在のタスクを一時停止し、キューにある別のタスクを処理できます。Awaitが完了すると、タスクは再開されます。これによりデッドロックが防止されますが、注意が必要です。awaitポイント間でactorの状態が変化する可能性があります。

swift
actor DataProcessor {
    var cache: [Int: String] = [:]
    
    func process(id: Int) async -> String {
        if let cached = cache[id] {
            return cached
        }
        // await — 再入ポイント
        let result = await fetchData(id: id)
        // await後にキャッシュが変更されている可能性があります — 再確認してください
        cache[id] = result
        return result
    }
}

開発者はreentrancyを考慮し、awaitポイントの後にactorの状態を確認する必要があります。典型的な間違いは、actor隔離が非同期の一時停止ポイントを越えて持続すると想定することです。実際には、awaitと次のステートメントの間で、状態が期待と異なる場合があります。

よくある質問

SwiftでActorはクラスとどう違うのですか?

Actorは自動的にデータ競合から状態を隔離し、外部アクセスにawaitを必要とします。クラスはそのような保護を提供しません — 開発者はロックやキューを介した同期を行う責任があります。Actorはactorからのみ継承し、クラスはクラスからのみ継承します。

Actorから継承できますか?

はい、actorは別のactorから継承できます。サブクラスは親のすべての隔離されたプロパティとメソッドを継承します。Actorはクラスから継承できません。クラスにはコンパイラレベルでのactor隔離メカニズムがないためです。

actor-isolatedコンテキストとは何ですか?

actor-isolatedコンテキストは、actorの可変状態への直接アクセスが許可されるコード領域です。隔離されているとマークされた(デフォルト)actorメソッド内では、awaitなしでプロパティを読み書きできます。コンパイラが隔離境界をチェックします。

Actorから外部コードにデータを渡すには?

Actorからのデータは、Sendable型を返す非同期メソッド、またはパラメータとして値を受け取るnonisolatedメソッドを介して渡されます。Sendable構造体としてactorの状態のスナップショットを返すasyncプロパティを作成することもできます。

Actorはプロトコル準拠をサポートしていますか?

はい、actorはプロトコルに準拠できます。プロトコルに隔離要件(actor-isolated)が含まれている場合、それらは自動的にactor隔離になります。プロトコルの非同期メソッドの場合、isolatedマーカーを使用して特定のactorで呼び出される必要があることを指定できます。

まとめ

  • Actorは自動データ隔離を備えた参照型で、Swift 5.5+で導入されました
  • 隔離はリクエストを順次処理するactor実行基盤を通じて機能します
  • 外部アクセスにはawaitが必要です — これにより競合がないことが保証されます
  • Nonisolatedを使用すると、隔離状態にアクセスしない同期メソッドを宣言できます
  • グローバルactor(@MainActor)は隔離を実行スレッド全体に拡張します
  • Reentrancyはawait中にactorへの再入を許可することでデッドロックを防ぎます

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください