Actor — egy Swift 5.5+-ban bevezetett típus, amely nyelvi szinten oldja meg az adatversenyhelyzetek problémáját. A kézi zárakkal és DispatchQueue sorokkal ellentétben az actor automatikusan izolálja állapotát és szinkronizálja a hozzáférést. Ez azt jelenti, hogy két szál nem módosíthatja egyszerre ugyanazt a tulajdonságot egy actor-típusban, ami külső erőfeszítés nélkül kúszöböli ki a versenyhelyzetet. A mechanizmus az actor-izoláció koncepcióján alapul, ahol a fordító nyomon követi az actor tulajdonságaihoz és metódusaihoz való hozzáférést. A Apple, 2025 szerint az actor modell a Swift konkurenciarendszerének alapvető része.
Főbb pontok
Actor — egy referenciatípus, hasonló az osztályhoz, de automatikus védelemmel a versenyhelyzetek ellen. A Swift 5.5-ben került bevezetésre a konkurenciarendszer részeként az async/await és Task mellett. Az actor garantálja, hogy változtatható állapota soha nem kerül egyidejűleg olvasásra vagy írásra különböző szálakból kifejezett szinkronizáció nélkül.
Az actor deklarálásához az actor kulcsszót használjuk, amelyet kapcsos zárójelek követnek a tagjaival. Szintaktikailag az actor hasonlít az osztályra, de a viselkedése győkeresen eltér.
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
self.balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func getBalance() -> Double {
return balance
}
}
A fordító automatikusan izolálja az actor összes tulajdonságát és metódusát úgy, hogy azok csak az actor-kontextuson belül érhetők el. Ha a balance-t az actoron kívülről próbáljuk elérni, a fordító hibát dob, ha a hívás nincs async-ként megjelölve.
Adatizoláció — az actor kulcsfogalma. Az actor az actor-végrehajtó (actor executor) mechanizmusán keresztül garantálja a kölcsönösen kizáró hozzáférést állapotához. Minden actor rendelkezik saját végrehajtóval, amely az izolált tagjaihoz való összes hozzáférést szekvenciálisan dolgozza fel.
Amikor az actoron kívüli kód meghívja annak metódusát, a hívás az actor-végrehajtó sorába kerül. Az actor egyszerre csak egy feladatot végez, ami garantálja a versenyhelyzetek hiányát. Ha két szál egyszerre hívja a deposit-ot, a második hívás megvárja az első befejeződését.
let account = BankAccount(initialBalance: 1000.0)
// Aszinkron hívás — az actoron kívülről szükséges
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()
Az actor metódusának minden hívása await-et igényel, mert az actor más feladattal lehet elfoglalva. Ez nem hiba, hanem tudatos tervezés a versenyhelyzetek elkerülésére. A Swift implicit módon aszinkronná teszi az actor tulajdonságainak getter-jeit és setter-eit, így egy tulajdonság olvasása is await-et igényel.
Actor és osztály referenciatípusok, de viselkedésük többszálas környezetben győkeresen eltér. Az osztály nem nyújt semmilyen automatikus védelmet a versenyhelyzetek ellen, míg az actor beépíti azt fordítói szinten a típusrendszeren keresztül.
| Jellemző | Actor | Class |
|---|---|---|
| Védelem a versenyhelyzet ellen | Automatikus, fordítói szinten | Kézi szinkronizációt igényel |
| Hozzáférés tulajdonságokhoz | Csak await-en keresztül külről | Közvetlen, szinkronizáció nélkül |
| Öröklés | Csak más actoroktól | Szabvány osztályöröklés |
| Protokoll megfelelés | Képes protokolloknak megfelelni | Szabvány |
| Teljesítmény | Alacsony többletterhelés izolációnál | Szinkronizáció nélkül — gyorsabb |
Az actor csak másik actor-tól örökődhet, és nem örökődhet osztályból. Ez tudatosan történt így, mert az osztály nem rendelkezik az actor-izoláció mechanizmusával, és a két modell keverése meghatározatlan viselkedéshez vezetne.
actor SavingsAccount: BankAccount {
func applyInterest(rate: Double) {
let interest = balance * rate
balance += interest
}
}
Aszinkron hívások — az actorral való interakció mechanizmusa külső kódból. Mivel az actor izolálja állapotát, minden külső hozzáférés a tagjaihoz await-et igényel. Ez lehetővé teszi a Swift számára, hogy garantálja: a hívó kód nem blokkolja a szálat, és az actor más kéréseket is feldolgozhat.
A deklarált actor-típusok mellett a Swift támogatja a globális actorokat — a @MainActor attribútumot, amely osztályokat, tulajdonságokat vagy metódusokat jelöl meg a főszálon való végrehajtásra. Ez különösen hasznos UIKit és SwiftUI használata esetén.
@MainActor
class ViewModel: ObservableObject {
@Published var title: String = ""
func updateTitle() {
// Ez a kód garantáltan a főszálon fut
title = "New Title"
}
}
A @MainActor használata kiküzöböli a DispatchQueue.main.async kézi meghívásának szükségességét, ami tisztábbá és biztonságosabbá teszi a kódot. A fordító ellenőrzi, hogy a főszálra váltás helyesen történik-e.
Nonisolated — egy kulcsszó, amely lehetővé teszi egy actor metódusának vagy számított tulajdonságának nem-izoláltként való megjelölését. Az ilyen tagok nem férnek hozzá az actor izolált állapotához, de await nélkül hívhatók az actoron kívülről.
A nonisolated metódusok hasznosak olyan számításokhoz, amelyek nem függnek az actor változtatható állapotától. Például a formatBalance metódus nem fér közvetlenül hozzá a balance-hoz, csak formázza az átadott értéket — egy ilyen metódus biztonságosan nonisolateddá tehető.
actor BankAccount {
private var balance: Double = 0
nonisolated func formatBalance(amount: Double) -> String {
return "$\(amount)"
}
}
A nonisolated tagok szinkron módon hajtódnak végre, és nem igényelnek await-et. Azonban nem tudják közvetlenül olvasni az actor izolált tulajdonságait. Ha egy nonisolated metódusnak értékre van szüksége az actor-ból, azt paraméterként kell átadni.
Reentrancy — egy mechanizmus, amely lehetővé teszi az actorba való újbelépést aszinkron hívás várakozása közben. Reentrancy nélkül az actor örökre blokkolódhatna, ha az egyik metódusa egy másikra várna, ami viszont az elsőre vár.
Amikor az actoron belüli kód await-et hajt végre, az actor felfüggeszti az aktuális feladatot, és feldolgozhat egy másikat, amely a sorban van. Az await befejeződése után a feladat folytatódik. Ez megakadályozza a deadlock-okat, de óvatosságot igényel: az actor állapota az await pontok között megváltozhat.
actor DataProcessor {
var cache: [Int: String] = [:]
func process(id: Int) async -> String {
if let cached = cache[id] {
return cached
}
// await — újbelépési pont
let result = await fetchData(id: id)
// A gyorsitótár megváltozhatott await után — ellenőrizze újra
cache[id] = result
return result
}
}
A fejlesztőknek figyelembe kell venniük a reentrancy-t, és ellenőrizniük kell az actor állapotát az await pontok után. Tipikus hiba annak feltételezése, hogy az actor-izoláció az aszinkron szünetek során is fennmarad. A gyakorlatban az await és a következő utasítás között az állapot eltérhet a várttól.
Gyakran Ismételt Kérdések
Actor automatikusan izolálja állapotát a versenyhelyzetektől, await-et igényelve a külső hozzáféréshez. Az osztály nem nyújt ilyen védelmet — a fejlesztő maga felel a szinkronizációért zárak és sorok segítségével. Az actor csak actor-tól örökődik, az osztály osztálytól.
Igen, az actor örökődhet másik actor-ból. Az alosztály megkapja a szülő összes izolált tulajdonságát és metódusát. Az actor nem örökődhet osztályból, mert az osztályok nem rendelkeznek actor-izolációs mechanizmussal fordítói szinten.
Actor-isolated kontextus — az a kódterüelet, ahol engedélyezett a közvetlen hozzáférés az actor változtatható állapotához. Az actor isolatedként megjelölt (alapértelmezett) metódusain belül a tulajdonságok await nélkül olvashatók és írhatók. A fordító ellenőrzi az izolációs határokat.
Az actor adatait async-metódusokon keresztül adjuk át, amelyek Sendable típusokat adnak vissza, vagy nonisolated metódusokon keresztül, amelyek értékeket fogadnak paraméterként. Létrehozható egy async tulajdonság is, amely az actor állapotának pillanatképét adja vissza Sendable struktúra formájában.
Igen, az actor képes protokolloknak megfelelni. Ha a protokoll izolált követelményeket (actor-isolated) tartalmaz, azok automatikusan actor-izolálttá válnak. A protokollok aszinkron metódusaihoz az isolated marker segítségével megadható, hogy egy adott actor-on kell őket meghívni.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is