Actor — este un tip introdus în Swift 5.5+ care rezolvă problema condițiilor de cursă a datelor la nivel de limbaj. Spre deosebire de blocările manuale și cozile DispatchQueue, actor își izolează automat starea și sincronizează accesul la aceasta. Aceasta înseamnă că două fire de execuție nu pot modifica simultan aceeași proprietate a unui tip actor, ceea ce exclude starea de cursă fără eforturi suplimentare din partea dezvoltatorului. Mecanismul se bazează pe conceptul de izolare a actorului, în care compilatorul urmărește accesul la proprietățile și metodele actorului. Potrivit Apple, 2025, modelul actor este o parte fundamentală a sistemului de concurență Swift.
Principalele
Actor — este un tip referențial, similar unei clase, dar cu protecție automată împotriva condițiilor de cursă. A fost introdus în Swift 5.5 ca parte a sistemului de concurență alături de async/await și Task. Actor garantează că starea sa modificabilă nu va fi niciodată citită sau scrisă simultan din fire diferite fără sincronizare explicită.
Pentru declararea unui actor se folosește cuvântul cheie actor, urmat de acolade cu membrii săi. Sintactic, actorul seamănă cu o clasă, dar comportamentul este radical diferit.
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
self.balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func getBalance() -> Double {
return balance
}
}
Compilatorul izolează automat toate proprietățile și metodele actorului, astfel încât acestea devin accesibile doar în contextul actorului. Încearcarea de a accesa balance din afara actorului va genera o eroare de compilare dacă apelul nu este marcat ca async.
Izolarea datelor — este conceptul cheie al actorului. Actorul garantează accesul mutual exclusiv la starea sa prin mecanismul executantului de actor (actor executor). Fiecare actor are propriul executant care procesează toate accesările la membrii săi izolați secvențial.
Când codul din afara actorului apelează metoda acestuia, apelul este plasat în coada executantului actorului. Actorul execută doar o singură sarcină simultan, ceea ce garantează absența condițiilor de cursă. Dacă două fire de execuție apelează simultan deposit, al doilea apel așteaptă finalizarea primului.
let account = BankAccount(initialBalance: 1000.0)
// Apel asincron — necesar din afara actorului
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()
Fiecare apel al metodei actorului necesită await, deoarece actorul poate fi ocupat cu o altă sarcină. Aceasta nu este o eroare, ci un design conștient care previne condițiile de cursă. Swift face implicit getterele și setterele proprietăților actorului async, deci citirea proprietății necesită de asemenea await.
Actor și clasa sunt tipuri referențiale, dar comportamentul lor în medii multi-threaded diferă radical. Clasa nu oferă nicio protecție automată împotriva condițiilor de cursă, în timp ce actorul o încorporează la nivel de compilator prin sistemul de tipuri.
| Caracteristică | Actor | Class |
|---|---|---|
| Protecție împotriva cursei | Automată, la nivel de compilator | Necesită sincronizare manuală |
| Acces la proprietăți | Doar prin await din exterior | Direct, fără sincronizare |
| Moștenire | Doar de la alți actori | Standard de clasă |
| Conformare protocol | Poate adera la protocoale | Standard |
| Performanță | Suprafață redusă la izolare | Fără sincronizare — mai rapid |
Actorul poate moșteni doar de la un alt actor și nu poate moșteni de la o clasă. Acest lucru este făcut conștient, deoarece clasa nu are mecanismul de izolare a actorului, iar amestecarea celor două modele ar duce la un comportament nedefinit.
actor SavingsAccount: BankAccount {
func applyInterest(rate: Double) {
let interest = balance * rate
balance += interest
}
}
Apelurile asincrone — sunt mecanismul de interacțiune cu actorul din codul extern. Deoarece actorul își izolează starea, orice acces la membrii săi din exterior necesită await. Acest lucru permite Swift să garanteze că codul apelant nu blochează firul de execuție și că actorul poate procesa alte cereri.
Pe lângă tipurile de actor declarate, Swift suportă actori globali — atributul @MainActor, care marchează clasele, proprietățile sau metodele ca fiind executate pe firul principal. Acest lucru este util în special la lucrul cu UIKit și SwiftUI.
@MainActor
class ViewModel: ObservableObject {
@Published var title: String = ""
func updateTitle() {
// Acest cod este garantat să ruleze pe firul principal
title = "New Title"
}
}
Utilizarea @MainActor elimină necesitatea de a apela manual DispatchQueue.main.async, ceea ce face codul mai curat și mai sigur. Compilatorul verifică dacă comutarea pe firul principal are loc corect.
Nonisolated — este un cuvânt cheie care permite marcarea unei metode sau proprietăți calculate a actorului ca neizolată. Astfel de membri nu au acces la starea izolată a actorului, dar pot fi apelați fără await din afara actorului.
Metodele nonisolated sunt utile pentru calcule care nu depind de starea modificabilă a actorului. De exemplu, metoda formatBalance nu accesează direct balance, ci doar formatează valoarea transmisă — o astfel de metodă poate fi făcută în siguranță nonisolated.
actor BankAccount {
private var balance: Double = 0
nonisolated func formatBalance(amount: Double) -> String {
return "$\(amount)"
}
}
Membrii nonisolated sunt executați sincron și nu necesită await. Cu toate acestea, nu pot citi direct proprietățile izolate ale actorului. Dacă o metodă nonisolated are nevoie de o valoare din actor, aceasta trebuie transmisă ca parametru.
Reentrancy — este un mecanism care permite reintrarea în actor în timpul așteptării unui apel asincron. Fără reentrancy, actorul s-ar putea bloca pentru totdeauna dacă o metodă a sa ar aștepta alta, care la rândul ei o așteaptă pe prima.
Când codul din interiorul actorului execută await, actorul își suspendă sarcina curentă și poate procesa alta plasată în coadă. După finalizarea await, sarcina este reluată. Acest lucru previne deadlock-urile, dar necesită prudență: starea actorului între punctele de await se poate modifica.
actor DataProcessor {
var cache: [Int: String] = [:]
func process(id: Int) async -> String {
if let cached = cache[id] {
return cached
}
// await — punct de reintrare
let result = await fetchData(id: id)
// Cache-ul s-ar putea fi schimbat după await — verifică din nou
cache[id] = result
return result
}
}
Dezvoltatorii trebuie să ia în considerare reentrancy și să verifice starea actorului după punctele de await. O greșeală tipică este presupunerea că izolarea actorului se menține prin pauzele asincrone. În practică, între await și următoarea instrucțiune, starea poate diferi de cea așteptată.
Întrebări frecvente
Actor își izolează automat starea de condițiile de cursă, necesitând await pentru accesul din exterior. Clasa nu oferă o astfel de protecție — dezvoltatorul este responsabil pentru sincronizare prin blocări sau cozi. Actorul moștenește doar de la actor, clasa — de la clasă.
Da, un actor poate moșteni de la un alt actor. Subclasa primește toate proprietățile și metodele izolate ale părintelui. Actorul nu poate moșteni de la o clasă, deoarece clasele nu au mecanismul de izolare a actorului la nivel de compilator.
Contextul actor-isolated — este o zonă de cod în care este permis accesul direct la starea modificabilă a actorului. în interiorul metodelor actorului marcate ca isolated (implicit), se pot citi și scrie proprietăți fără await. Compilatorul verifică granițele de izolare.
Datele din actor sunt transmise prin metode async care returnează tipuri Sendable, sau prin metode nonisolated care primesc valori ca parametri. Se poate crea, de asemenea, o proprietate async care returnează o imagine a stării actorului sub forma unei structuri Sendable.
Da, actorul poate adera la protocoale. Dacă protocolul conține cerințe izolate (actor-isolated), acestea devin automat izolate de actor. Pentru metodele asincrone din protocoale, se poate specifica cu ajutorul markerului isolated că trebuie apelate pe un anumit actor.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și