Actor — cos'è, isolamento dei dati e concorrenza

Autore: IT Sectr Pubblicato: 2026-06-18 Tempo di lettura: 11 min

Actor è un tipo introdotto in Swift 5.5+ che risolve il problema delle race condition a livello di linguaggio. A differenza dei lock manuali e delle code DispatchQueue, un actor isola automaticamente il suo stato e sincronizza l'accesso ad esso. Ciò significa che due thread non possono modificare simultaneamente la stessa proprietà di un tipo actor, eliminando le race condition senza sforzo aggiuntivo da parte dello sviluppatore. Il meccanismo si basa sul concetto di isolamento dell'actor, dove il compilatore traccia l'accesso alle proprietà e ai metodi dell'actor. Secondo Apple, 2025, il modello actor è una parte fondamentale del sistema di concorrenza di Swift.

Punti Chiave

  • Actor è un tipo che isola automaticamente il suo stato dalle race condition a livello di compilatore
  • L'isolamento garantisce che un solo thread acceda all'istanza dell'actor alla volta
  • Le chiamate ai metodi dell'actor vengono eseguite in modo asincrono tramite il meccanismo async/await
  • Nonisolated consente di disabilitare selettivamente l'isolamento per singoli membri dell'actor
  • Reentrancy permette la rientranza in un actor, prevenendo i deadlock

Cos'è un Actor in Swift?

Actor è un tipo riferimento, simile a una classe, ma con protezione automatica contro le race condition. È stato introdotto in Swift 5.5 come parte del sistema di concorrenza insieme a async/await e Task. Un actor garantisce che il suo stato mutabile non venga mai letto o scritto simultaneamente da thread diversi senza sincronizzazione esplicita.

Sintassi di dichiarazione dell'Actor

Per dichiarare un actor, si usa la parola chiave actor seguita da parentesi graffe con i suoi membri. Sintatticamente, un actor assomiglia a una classe, ma il suo comportamento differisce radicalmente.

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

Il compilatore isola automaticamente tutte le proprietà e i metodi dell'actor in modo che siano accessibili solo all'interno del contesto dell'actor. Quando si tenta di accedere a balance dall'esterno dell'actor, il compilatore genererà un errore se la chiamata non è contrassegnata come async.

Come funziona l'isolamento dei dati in Actor

L'isolamento dei dati è il concetto chiave di un actor. Un actor garantisce l'accesso mutuamente esclusivo al suo stato attraverso il meccanismo dell'esecutore dell'actor. Ogni actor ha il proprio esecutore che elabora sequenzialmente tutti gli accessi ai suoi membri isolati.

Come funziona l'isolamento

Quando il codice esterno a un actor chiama il suo metodo, la chiamata viene inserita nella coda dell'esecutore dell'actor. L'actor esegue un solo compito alla volta, garantendo l'assenza di race condition. Se due thread chiamano deposit contemporaneamente, la seconda chiamata attende il completamento della prima.

swift
let account = BankAccount(initialBalance: 1000.0)
// Chiamata asincrona — richiesta dall'esterno dell'actor
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()

Ogni chiamata a un metodo dell'actor richiede await perché l'actor potrebbe essere occupato con un'altra attività. Questo non è un bug, ma un design consapevole che previene le race condition. Swift rende implicitamente async i getter e setter delle proprietà dell'actor, quindi anche la lettura di una proprietà richiede await.

Principali differenze tra Actor e Classe

Actor e classe sono entrambi tipi riferimento, ma il loro comportamento in un ambiente multi-thread differisce drasticamente. Una classe non fornisce alcuna protezione automatica contro le race condition, mentre un actor la incorpora a livello di compilatore attraverso il sistema dei tipi.

CaratteristicaActorClasse
Protezione dalle raceAutomatica, a livello di compilatoreRichiede sincronizzazione manuale
Accesso alle proprietàSolo tramite await dall'esternoDiretto, senza sincronizzazione
EreditarietàSolo da altri actorEreditarietà di classe standard
Conformità ai protocolliPuò essere conforme ai protocolliStandard
PrestazioniBasso overhead con isolamentoPiù veloce senza sincronizzazione

Ereditarietà dell'Actor

Un actor può ereditare solo da un altro actor e non può ereditare da una classe. Questo è intenzionale perché una classe non ha il meccanismo di isolamento dell'actor e mescolare i tipi romperebbe le garanzie di sicurezza.

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

Chiamate asincrone ai metodi di Actor

Le chiamate asincrone sono il meccanismo per interagire con un actor da codice esterno. Poiché un actor isola il suo stato, qualsiasi accesso ai suoi membri dall'esterno richiede await. Ciò consente a Swift di garantire che il codice chiamante non blocchi il thread e che l'actor possa elaborare altre richieste.

Actor globali e locali

Oltre ai tipi di actor dichiarati, Swift supporta gli actor globali — l'attributo @MainActor, che contrassegna classi, proprietà o metodi come eseguibili sul thread principale. Questo è particolarmente utile per il codice dell'interfaccia utente nelle applicazioni iOS.

swift
@MainActor
class ViewModel: ObservableObject {
    @Published var title: String = ""
    
    func updateTitle() {
        // Questo codice è garantito per essere eseguito sul thread principale
        title = "New Title"
    }
}

L'uso di @MainActor elimina la necessità di chiamare manualmente DispatchQueue.main.async, rendendo il codice più pulito e sicuro. Il compilatore verifica che il passaggio al thread principale avvenga correttamente.

Contesti Nonisolated e Actor-Isolated

Nonisolated è una parola chiave che consente di contrassegnare un metodo o una proprietà calcolata di un actor come non isolato. Questi membri non hanno accesso allo stato isolato dell'actor, ma possono essere chiamati senza await dall'esterno dell'actor.

Funzioni Nonisolated

I metodi nonisolated sono utili per calcoli che non dipendono dallo stato mutabile dell'actor. Ad esempio, il metodo formatBalance non accede direttamente a balance, ma formatta solo il valore passato — un tale metodo è sicuro da rendere nonisolated.

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

I membri nonisolated vengono eseguiti in modo sincrono e non richiedono await. Tuttavia, non possono leggere direttamente le proprietà isolate dell'actor. Se un metodo nonisolated ha bisogno di un valore dall'actor, deve essere passato come parametro.

Reentrancy e rientranza

Reentrancy è un meccanismo che consente la rientranza in un actor durante l'attesa di una chiamata asincrona. Senza reentrancy, un actor potrebbe bloccarsi per sempre se uno dei suoi metodi attendesse un altro che a sua volta attendesse il primo.

Come funziona la rientranza

Quando il codice all'interno di un actor esegue await, l'actor sospende l'attività corrente e può elaborare un'altra attività in coda. Dopo il completamento di await, l'attività riprende. Questo previene i deadlock ma richiede cautela: lo stato dell'actor tra i punti di await può cambiare.

swift
actor DataProcessor {
    var cache: [Int: String] = [:]
    
    func process(id: Int) async -> String {
        if let cached = cache[id] {
            return cached
        }
        // await — punto di rientranza
        let result = await fetchData(id: id)
        // La cache potrebbe essere cambiata dopo await — ricontrollare
        cache[id] = result
        return result
    }
}

Gli sviluppatori devono tenere conto della rientranza e verificare lo stato dell'actor dopo i punti di await. Un errore tipico è presumere che l'isolamento dell'actor persista attraverso i punti di sospensione asincroni. In pratica, tra await e l'istruzione successiva, lo stato potrebbe differire da quanto previsto.

Domande Frequenti

In cosa differisce un Actor da una classe in Swift?

Actor isola automaticamente il suo stato dalle race condition, richiedendo await per l'accesso esterno. Una classe non offre tale protezione — lo sviluppatore è responsabile della sincronizzazione tramite lock o code. Un actor eredita solo da un actor, una classe da una classe.

Si può ereditare da un Actor?

Sì, un actor può ereditare da un altro actor. La sottoclasse ottiene tutte le proprietà e i metodi isolati del genitore. Un actor non può ereditare da una classe perché le classi non hanno il meccanismo di isolamento dell'actor a livello di compilatore.

Cos'è un contesto actor-isolated?

Un contesto actor-isolated è un'area di codice in cui è consentito l'accesso diretto allo stato mutabile dell'actor. All'interno dei metodi dell'actor contrassegnati come isolati (per impostazione predefinita), è possibile leggere e scrivere proprietà senza await. Il compilatore controlla i confini dell'isolamento.

Come passare dati da un Actor al codice esterno?

I dati da un actor vengono passati tramite metodi async che restituiscono tipi Sendable, o tramite metodi nonisolated che accettano valori come parametri. Si può anche creare una proprietà async che restituisce un'istantanea dello stato dell'actor come struttura Sendable.

Un Actor supporta la conformità ai protocolli?

Sì, un actor può essere conforme ai protocolli. Se un protocollo contiene requisiti isolati (actor-isolated), diventano automaticamente isolati dall'actor. Per i metodi asincroni nei protocolli, è possibile specificare che devono essere chiamati su un actor specifico utilizzando il marcatore isolated.

Riepilogo

  • Actor è un tipo riferimento con isolamento automatico dei dati, introdotto in Swift 5.5+
  • L'isolamento funziona tramite un esecutore dell'actor che elabora le richieste in sequenza
  • L'accesso esterno a un actor richiede await — questo garantisce l'assenza di race condition
  • Nonisolated consente di dichiarare metodi sincroni che non accedono allo stato isolato
  • Gli actor globali (@MainActor) estendono l'isolamento all'intero thread di esecuzione
  • Reentrancy previene i deadlock consentendo la rientranza in un actor durante await

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche