Aktor — co to jest, izolacja danych i współbieżność

Autor: IT Sectr Opublikowano: 2026-06-18 Czas czytania: 11 min

Actor — to typ wprowadzony w Swift 5.5+, który rozwiązuje problem wyścigów danych na poziomie języka. W przeciwieństwie do ręcznych blokad i kolejek DispatchQueue, actor automatycznie izoluje swój stan i synchronizuje dostęp do niego. Oznacza to, że dwa wątki nie mogą jednocześnie zmienić tej samej właściwości typu actor, co eliminuje stan wyścigu bez dodatkowego wysiłku ze strony programisty. Mechanizm opiera się na koncepcji izolacji aktora, gdzie kompilator śledzi dostęp do właściwości i metod aktora. Według Apple, 2025, model aktora jest fundamentalną częścią systemu współbieżności Swift.

Najważniejsze

  • Actor — typ automatycznie izolujący swój stan od wyścigów danych na poziomie kompilatora
  • Izolacja gwarantuje, że tylko jeden wątek jednocześnie uzyskuje dostęp do instancji aktora
  • Wywołania metod aktora są wykonywane asynchronicznie przez mechanizm async/await
  • Nonisolated pozwala wybiórczo wyłączyć izolację dla poszczególnych członków aktora
  • Reentrancy umożliwia ponowne wejście do aktora, zapobiegając wzajemnym blokadom

Czym jest Actor w Swift?

Actor — to typ referencyjny, podobny do klasy, ale z automatyczną ochroną przed wyścigami danych. Został wprowadzony w Swift 5.5 jako część systemu współbieżności wraz z async/await i Task. Actor gwarantuje, że jego zmienny stan nigdy nie zostanie jednocześnie odczytany lub zapisany z różnych wątków bez jawnej synchronizacji.

Składnia deklaracji Actor

Do deklaracji aktora używa się słowa kluczowego actor, po którym następują nawiasy klamrowe z jego członkami. Składniowo actor przypomina klasę, ale zachowanie radykalnie się różni.

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

Kompilator automatycznie izoluje wszystkie właściwości i metody aktora, tak że stają się dostępne tylko wewnątrz kontekstu aktora. Próba dostępu do balance spoza aktora spowoduje błąd kompilacji, jeśli wywołanie nie jest oznaczone jako async.

Jak działa izolacja danych w Actor

Izolacja danych — to kluczowa koncepcja aktora. Actor gwarantuje wzajemnie wykluczający dostęp do swojego stanu poprzez mechanizm wykonawcy aktora (actor executor). Każdy actor ma własnego wykonawcę, który przetwarza wszystkie dostępy do jego izolowanych członków sekwencyjnie.

Zasada działania izolacji

Gdy kod spoza aktora wywołuje jego metodę, wywołanie jest umieszczane w kolejce wykonawcy aktora. Actor wykonuje tylko jedno zadanie jednocześnie, co gwarantuje brak wyścigów. Jeśli dwa wątki jednocześnie wywołają deposit, drugie wywołanie czeka na zakończenie pierwszego.

swift
let account = BankAccount(initialBalance: 1000.0)
// Wywołanie asynchroniczne — wymagane spoza aktora
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()

Każde wywołanie metody aktora wymaga await, ponieważ actor może być zajęty innym zadaniem. To nie jest błąd, lecz świadomy projekt zapobiegający wyścigom. Swift niejawnie czyni gettery i settery właściwości aktora asynchronicznymi, dlatego odczyt właściwości również wymaga await.

Główne różnice między Actor a klasą

Actor i klasa są typami referencyjnymi, ale ich zachowanie w środowisku wielowątkowym radykalnie się różni. Klasa nie zapewnia żadnej automatycznej ochrony przed wyścigami, podczas gdy actor wbudowuje ją na poziomie kompilatora poprzez system typów.

CechaActorClass
Ochrona przed wyścigamiAutomatyczna, na poziomie kompilatoraWymaga ręcznej synchronizacji
Dostęp do właściwościTylko przez await z zewnątrzBezpośredni, bez synchronizacji
DziedziczenieTylko od innych aktorówStandardowe klasowe
Zgodność z protokołamiMoże zgodnie z protokołamiStandardowe
WydajnośćNiski narzut przy izolacjiBez synchronizacji — szybsze

Dziedziczenie Actor

Actor może dziedziczyć tylko po innym actorze i nie może dziedziczyć po klasie. Jest to świadome działanie, ponieważ klasa nie ma mechanizmu izolacji aktora, a mieszanie dwóch modeli prowadziłoby do nieokreślonego zachowania.

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

Asynchroniczne wywołania metod Actor

Wywołania asynchroniczne — to mechanizm interakcji z aktorem z zewnętrznego kodu. Ponieważ actor izoluje swój stan, każdy dostęp do jego członków z zewnątrz wymaga await. Pozwala to Swift zagwarantować, że wywołujący kod nie blokuje wątku, a actor może przetwarzać inne żądania.

Globalne i lokalne aktory

Oprócz zadeklarowanych typów aktora, Swift obsługuje globalne aktory — atrybut @MainActor, który oznacza klasy, właściwości lub metody jako wykonywane na głównym wątku. Jest to szczególnie przydatne podczas pracy z UIKit i SwiftUI.

swift
@MainActor
class ViewModel: ObservableObject {
    @Published var title: String = ""
    
    func updateTitle() {
        // Ten kod jest gwarantowanie wykonywany na głównym wątku
        title = "New Title"
    }
}

Używanie @MainActor eliminuje konieczność ręcznego wywoływania DispatchQueue.main.async, co czyni kod czystszym i bezpieczniejszym. Kompilator sprawdza, czy przełączenie na główny wątek następuje poprawnie.

Nonisolated i konteksty actor-isolated

Nonisolated — to słowo kluczowe umożliwiające oznaczenie metody lub właściwości obliczanej aktora jako nieizolowanej. Takie członki nie mają dostępu do stanu izolowanego aktora, ale mogą być wywoływane bez await z zewnątrz aktora.

Funkcje Nonisolated

Metody nonisolated są przydatne do obliczeń niezależnych od zmiennego stanu aktora. Na przykład metoda formatBalance nie ma bezpośredniego dostępu do balance, a jedynie formatuje przekazaną wartość — taką metodę można bezpiecznie uczynić nonisolated.

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

Członki nonisolated są wykonywane synchronicznie i nie wymagają await. Nie mogą jednak bezpośrednio odczytywać izolowanych właściwości aktora. Jeśli metoda nonisolated potrzebuje wartości z aktora, należy ją przekazać jako parametr.

Reentrancy i ponowne wejście

Reentrancy — to mechanizm umożliwiający ponowne wejście do aktora podczas oczekiwania na wywołanie asynchroniczne. Bez reentrancy actor mógłby zablokować się na zawsze, gdyby jedna jego metoda czekała na drugą, która z kolei czeka na pierwszą.

Jak działa reentrancy

Gdy kod wewnątrz aktora wykonuje await, actor zawiesza bieżące zadanie i może przetworzyć inne umieszczone w kolejce. Po zakończeniu await zadanie jest wznawiane. Zapobiega to deadlockom, ale wymaga ostrożności: stan aktora między punktami await może się zmienić.

swift
actor DataProcessor {
    var cache: [Int: String] = [:]
    
    func process(id: Int) async -> String {
        if let cached = cache[id] {
            return cached
        }
        // await — punkt ponownego wejścia
        let result = await fetchData(id: id)
        // Pamięć podręczna mogła się zmienić po await — sprawdź ponownie
        cache[id] = result
        return result
    }
}

Programiści muszą uwzględniać reentrancy i sprawdzać stan aktora po punktach await. Typowym błędem jest założenie, że izolacja aktora utrzymuje się przez asynchroniczne pauzy. W praktyce między await a następną instrukcją stan może odbiegać od oczekiwanego.

Często zadawane pytania

Czym różni się Actor od klasy w Swift?

Actor automatycznie izoluje swój stan od wyścigów danych, wymagając await do dostępu z zewnątrz. Klasa nie zapewnia takiej ochrony — programista sam odpowiada za synchronizację przez blokady lub kolejki. Actor dziedziczy tylko po actorze, klasa — po klasie.

Czy można dziedziczyć po Actor?

Tak, actor może dziedziczyć po innym actorze. Podklasa otrzymuje wszystkie izolowane właściwości i metody rodzica. Actor nie może dziedziczyć po klasie, ponieważ klasy nie mają mechanizmu izolacji aktora na poziomie kompilatora.

Czym jest kontekst actor-isolated?

Kontekst actor-isolated — to obszar kodu, w którym dozwolony jest bezpośredni dostęp do zmiennego stanu aktora. Wewnątrz metod aktora oznaczonych jako isolated (domyślnie) można czytać i pisać właściwości bez await. Kompilator sprawdza granice izolacji.

Jak przekazać dane z Actor do zewnętrznego kodu?

Dane z aktora są przekazywane przez metody async, które zwracają typy Sendable, lub przez metody nonisolated przyjmujące wartości jako parametry. Można również utworzyć właściwość async, która zwraca migawkę stanu aktora w postaci struktury Sendable.

Czy Actor obsługuje dziedziczenie protokołów?

Tak, actor może zgodnie z protokołami. Jeśli protokół zawiera wymagania izolowane (actor-isolated), stają się one automatycznie izolowane przez aktora. W przypadku metod asynchronicznych w protokołach można określić, że mają być wywoływane na konkretnym actorze za pomocą znacznika isolated.

Podsumowanie

  • Actor — typ referencyjny z automatyczną izolacją danych, wprowadzony w Swift 5.5+
  • Izolacja działa poprzez wykonawcę aktora, który przetwarza żądania sekwencyjnie
  • Dostęp zewnętrzny do aktora wymaga await — gwarantuje to brak wyścigów
  • Nonisolated pozwala deklarować metody synchroniczne, które nie mają dostępu do izolowanego stanu
  • Globalne aktory (@MainActor) rozprzestrzeniają izolację na cały wątek wykonania
  • Reentrancy zapobiega deadlockom, umożliwiając ponowne wejście do aktora podczas await

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również