Actor es un tipo introducido en Swift 5.5+ que resuelve el problema de las condiciones de carrera a nivel de lenguaje. A diferencia de los bloqueos manuales y las colas DispatchQueue, un actor aísla automáticamente su estado y sincroniza el acceso al mismo. Esto significa que dos hilos no pueden modificar simultáneamente la misma propiedad de un tipo actor, eliminando las condiciones de carrera sin esfuerzo adicional por parte del desarrollador. El mecanismo se basa en el concepto de aislamiento de actor, donde el compilador rastrea el acceso a las propiedades y métodos del actor. Según Apple, 2025, el modelo de actor es una parte fundamental del sistema de concurrencia de Swift.
Puntos Clave
Actor es un tipo de referencia, similar a una clase, pero con protección automática contra condiciones de carrera. Fue introducido en Swift 5.5 como parte del sistema de concurrencia junto con async/await y Task. Un actor garantiza que su estado mutable nunca sea leído o escrito simultáneamente desde diferentes hilos sin sincronización explícita.
Para declarar un actor, se usa la palabra clave actor seguida de llaves con sus miembros. Sintácticamente, un actor se asemeja a una clase, pero su comportamiento difiere radicalmente.
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
self.balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func getBalance() -> Double {
return balance
}
}
El compilador aísla automáticamente todas las propiedades y métodos del actor para que solo sean accesibles dentro del contexto del actor. Al intentar acceder a balance desde fuera del actor, el compilador generará un error si la llamada no está marcada como async.
El aislamiento de datos es el concepto clave de un actor. Un actor garantiza el acceso mutuamente exclusivo a su estado a través del mecanismo del ejecutor del actor. Cada actor tiene su propio ejecutor que procesa todos los accesos a sus miembros aislados de forma secuencial.
Cuando el código fuera de un actor llama a su método, la llamada se coloca en la cola del ejecutor del actor. El actor ejecuta solo una tarea a la vez, garantizando la ausencia de condiciones de carrera. Si dos hilos llaman a deposit simultáneamente, la segunda llamada espera a que la primera termine.
let account = BankAccount(initialBalance: 1000.0)
// Llamada asíncrona — requerida desde fuera del actor
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()
Cada llamada a un método del actor requiere await porque el actor puede estar ocupado con otra tarea. Esto no es un error, sino un diseño consciente que evita condiciones de carrera. Swift hace implícitamente que los getters y setters de las propiedades del actor sean async, por lo que leer una propiedad también requiere await.
Actor y clase son ambos tipos de referencia, pero su comportamiento en un entorno multiproceso difiere drásticamente. Una clase no proporciona ninguna protección automática contra condiciones de carrera, mientras que un actor la incorpora a nivel de compilador a través del sistema de tipos.
| Característica | Actor | Clase |
|---|---|---|
| Protección contra carreras | Automática, a nivel de compilador | Requiere sincronización manual |
| Acceso a propiedades | Solo mediante await desde fuera | Directo, sin sincronización |
| Herencia | Solo de otros actores | Herencia de clases estándar |
| Conformidad con protocolos | Puede cumplir con protocolos | Estándar |
| Rendimiento | Baja sobrecarga con aislamiento | Más rápido sin sincronización |
Un actor solo puede heredar de otro actor y no puede heredar de una clase. Esto es intencional porque una clase carece del mecanismo de aislamiento de actor, y mezclar tipos rompería las garantías de seguridad.
actor SavingsAccount: BankAccount {
func applyInterest(rate: Double) {
let interest = balance * rate
balance += interest
}
}
Las llamadas asíncronas son el mecanismo para interactuar con un actor desde código externo. Dado que un actor aísla su estado, cualquier acceso a sus miembros desde fuera requiere await. Esto permite que Swift garantice que el código llamante no bloquee el hilo y que el actor pueda procesar otras solicitudes.
Además de los tipos de actor declarados, Swift admite actores globales — el atributo @MainActor, que marca clases, propiedades o métodos como ejecutables en el hilo principal. Esto es especialmente útil para el código de UI en aplicaciones de iOS.
@MainActor
class ViewModel: ObservableObject {
@Published var title: String = ""
func updateTitle() {
// Este código está garantizado para ejecutarse en el hilo principal
title = "New Title"
}
}
Usar @MainActor elimina la necesidad de llamar manualmente a DispatchQueue.main.async, haciendo el código más limpio y seguro. El compilador verifica que el cambio al hilo principal ocurra correctamente.
Nonisolated es una palabra clave que permite marcar un método o propiedad calculada de un actor como no aislado. Dichos miembros no tienen acceso al estado aislado del actor, pero pueden ser llamados sin await desde fuera del actor.
Los métodos nonisolated son útiles para cálculos que no dependen del estado mutable del actor. Por ejemplo, el método formatBalance no accede a balance directamente, sino que solo formatea el valor pasado — dicho método es seguro para hacerlo nonisolated.
actor BankAccount {
private var balance: Double = 0
nonisolated func formatBalance(amount: Double) -> String {
return "$\(amount)"
}
}
Los miembros nonisolated se ejecutan de forma síncrona y no requieren await. Sin embargo, no pueden leer propiedades aisladas del actor directamente. Si un método nonisolated necesita un valor del actor, debe ser pasado como parámetro.
Reentrancy es un mecanismo que permite la reentrada en un actor mientras se espera una llamada asíncrona. Sin reentrancy, un actor podría bloquearse para siempre si uno de sus métodos esperara a otro que a su vez esperara al primero.
Cuando el código dentro de un actor ejecuta await, el actor suspende la tarea actual y puede procesar otra tarea en cola. Después de que await se completa, la tarea se reanuda. Esto evita bloqueos mutuos, pero requiere precaución: el estado del actor entre puntos de await puede cambiar.
actor DataProcessor {
var cache: [Int: String] = [:]
func process(id: Int) async -> String {
if let cached = cache[id] {
return cached
}
// await — punto de reentrada
let result = await fetchData(id: id)
// La caché puede haber cambiado después de await — volver a verificar
cache[id] = result
return result
}
}
Los desarrolladores deben tener en cuenta la reentrada y verificar el estado del actor después de los puntos de await. Un error típico es asumir que el aislamiento del actor persiste a través de puntos de suspensión asíncronos. En la práctica, entre await y la siguiente declaración, el estado puede diferir de lo esperado.
Preguntas Frecuentes
Actor aísla automáticamente su estado de las condiciones de carrera, requiriendo await para el acceso externo. Una clase no proporciona dicha protección — el desarrollador es responsable de la sincronización mediante bloqueos o colas. Un actor solo hereda de un actor, una clase de una clase.
Sí, un actor puede heredar de otro actor. La subclase obtiene todas las propiedades y métodos aislados del padre. Un actor no puede heredar de una clase porque las clases carecen del mecanismo de aislamiento de actor a nivel de compilador.
Un contexto actor-isolated es un área de código donde se permite el acceso directo al estado mutable del actor. Dentro de los métodos del actor marcados como aislados (por defecto), se pueden leer y escribir propiedades sin await. El compilador verifica los límites del aislamiento.
Los datos de un actor se pasan a través de métodos async que devuelven tipos Sendable, o mediante métodos nonisolated que aceptan valores como parámetros. También se puede crear una propiedad async que devuelva una instantánea del estado del actor como una estructura Sendable.
Sí, un actor puede cumplir con protocolos. Si un protocolo contiene requisitos aislados (actor-isolated), automáticamente se vuelven aislados del actor. Para métodos asíncronos en protocolos, se puede especificar que deben ser llamados en un actor específico usando el marcador isolated.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también