Actor — какво е, изолация на данни и конкурентност

Автор: IT Sectr Публикувано: 2026-06-18 Време за четене: 11 мин

Actor — е тип, представен в Swift 5.5+, който решава проблема с състезание на данни на езиково ниво. За разлика от ръчните заключвания и опашките DispatchQueue, actor автоматично изолира състоянието си и синхронизира достъпа до него. Това означава, че две нишки не могат едновременно да променят едно и също свойство на тип actor, което елиминира състезанието без допълнително усилие от страна на разработчика. Механизмът се базира на концепцията изолация на actor, където компилаторът проследява достъпа до свойствата и методите на actor. Според Apple, 2025, моделът на actor е фундаментална част от система за конкурентност на Swift.

Основни моменти

  • Actor — тип, който автоматично изолира състоянието си от състезание на данни на ниво компилатор
  • Изолация гарантира, че само една нишка едновременно има достъп до инстанцията на actor
  • Призови на методите на actor се изпълняват асинхронно чрез механизма async/await
  • Nonisolated позволява избирателно да изключите изолацията за отделни членове на actor
  • Reentrancy позволява повторно вхождане в actor, предотвратявайки взаимни блокировки

Какво е Actor в Swift?

Actor — е референтен тип, подобен на клас, но с автоматична защита срещу състезание на данни. Беше представен в Swift 5.5 като част от система за конкурентност заедно с async/await и Task. Actor гарантира, че променливото му състояние никога няма да бъде едновременно четено или записвано от различни нишки без изрична синхронизация.

Синтаксис на деклариране на Actor

За деклариране на actor се използва ключовата дума actor, следвана от фигурни скоби с неговите членове. Синтактически, actor напомня клас, но поведението му е радикално различно.

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

Компилаторът автоматично изолира всички свойства и методи на actor така, че те са достъпни само в контекста на actor. Опит за достъп до balance отвън actor ще предизвика грешка на компилатора, ако призовът не е маркиран като async.

Как работи изолацията на данни в Actor

Изолация на данни — е ключовата концепция на actor. Actor гарантира взаимно изключващ достъп до състоянието си чрез механизма на изпълнителя на actor (actor executor). Всеки actor има собствен изпълнител, който обработва всички достъпи до неговите изолирани членове последователно.

Принцип на действие на изолацията

Когато код извън actor вика неговия метод, призовът се поставя в опашката на изпълнителя на actor. Actor изпълнява само една задача едновременно, което гарантира липсата на състезания. Ако две нишки едновременно викат deposit, вторият призов чака завършването на първия.

swift
let account = BankAccount(initialBalance: 1000.0)
// Асинхронен призов — изисква се отвън actor
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()

Всеки призов на метод на actor изисква await, защото actor може да е зает с друга задача. Това не е грешка, а съзнателен дизайн за предотвратяване на състезания. Swift имплицитно прави getter и setter на свойствата на actor async, така че четенето на свойство също изисква await.

Основни разлики между Actor и клас

Actor и класът са референтни типове, но поведението им в многонишкова среда се различава радикално. Класът не осигурява никаква автоматична защита срещу състезания, докато actor го вгражда на ниво компилатор чрез системата от типове.

ХарактеристикаActorClass
Защита от състезаниеАвтоматична, на ниво компилаторИзисква ръчна синхронизация
Достъп до свойстваСамо чрез await отвънДиректен, без синхронизация
НаследяванеСамо от други actorСтандартно класово
Съвместимост с протоколиМоже да съвпада с протоколиСтандартно
ПроизводителностНисък оверхейд при изолацияБез синхронизация — по-бързо

Наследяване на Actor

Actor може да наследява само от друг actor и не може да наследява от клас. Това е направено съзнателно, тъй като класът няма механизъм за изолация на actor и смесването на двата модела би довело до недефинирано поведение.

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

Асинхронни призови на методите на Actor

Асинхронни призови — са механизъм за взаимодействие с actor от външен код. Тъй като actor изолира състоянието си, всяка промяна на неговите членове отвън изисква await. Това позволява на Swift да гарантира, че викащият код не блокира нишката и че actor може да обработва други заявки.

Глобални и локални actor

Освен декларираните типове actor, Swift поддържа глобални actor — атрибута @MainActor, който маркира класове, свойства или методи като изпълнявани на главната нишка. Това е особено полезно при работа с UIKit и SwiftUI.

swift
@MainActor
class ViewModel: ObservableObject {
    @Published var title: String = ""
    
    func updateTitle() {
        // Този код гарантирано се изпълнява на главната нишка
        title = "New Title"
    }
}

Използването на @MainActor премахва необходимостта от ръчно викане на DispatchQueue.main.async, което прави кода по-чист и по-сигурен. Компилаторът проверява дали преключването към главната нишка се извършва правилно.

Nonisolated и actor-isolated контекстове

Nonisolated — е ключова дума, която позволява да маркирате метод или изчисляемо свойство на actor като неизолирано. Такива членове нямат достъп до изолираното състояние на actor, но могат да бъдат викани без await отвън actor.

Nonisolated функции

Nonisolated методите са полезни за изчисления, които не зависят от променливото състояние на actor. Например, методът formatBalance няма пряк достъп до balance, а само форматира подадената стойност — такъв метод може безопасно да бъде направен nonisolated.

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

Nonisolated членовете се изпълняват синхронно и не изискват await. Въпреки това, те не могат директно да четат изолираните свойства на actor. Ако nonisolated метод се нуждае от стойност от actor, тя трябва да бъде предадена като параметър.

Reentrancy и повторно вхождане

Reentrancy — е механизъм, който позволява повторно вхождане в actor по време на чакане за асинхронен призов. Без reentrancy, actor би могъл да се блокира завинаги, ако един негов метод чака друг, който от своя страна чака първия.

Как работи reentrancy

Когато код в актьора изпълнява await, актьорът спира текущата задача и може да обработи друга, поставена в опашката. След завършване на await, задачата се подновява. Това предотвратява deadlock, но изисква внимание: състоянието на actor между точките на await може да се промени.

swift
actor DataProcessor {
    var cache: [Int: String] = [:]
    
    func process(id: Int) async -> String {
        if let cached = cache[id] {
            return cached
        }
        // await — точка на повторно вхождане
        let result = await fetchData(id: id)
        // Кешът може да се е променил след await — проверете отново
        cache[id] = result
        return result
    }
}

Разработчиците трябва да вземат предвид reentrancy и да проверяват състоянието на actor след точките на await. Типична грешка е предположението, че изолацията на actor се запазва през асинхронни паузи. На практика, между await и следващата инструкция, състоянието може да се различава от очакваното.

Често задавани въпроси

Каква е разликата между Actor и клас в Swift?

Actor автоматично изолира състоянието си от състезание на данни, изисквайки await за достъп отвън. Класът не предоставя такава защита — разработчикът сам отговаря за синхронизацията чрез заключвания или опашки. Actor наследява само от actor, класът от клас.

Може ли да се наследява от Actor?

Да, actor може да наследява от друг actor. Подкласът получава всички изолирани свойства и методи на родителя. Actor не може да наследява от клас, тъй като класовете нямат механизъм за изолация на actor на ниво компилатор.

Какво е actor-isolated контекст?

actor-isolated контекст — е област от код, в която е разрешен пряк достъп до променливото състояние на actor. В методите на actor, маркирани като isolated (по подразбиране), може да четете и пишете свойства без await. Компилаторът проверява границите на изолация.

Как да предам данни от Actor на въшкен код?

Данните от actor се предават чрез async-методи, които връщат Sendable типове, или чрез nonisolated методи, приемащи стойности като параметри. Може също да създадете async свойство, което връща моментална снимка на състоянието на actor под формата на Sendable структура.

Поддържа ли Actor наследяване на протоколи?

Да, actor може да съвпада с протоколи. Ако протоколът съдържа изолирани изисквания (actor-isolated), те автоматично стават actor-изолирани. За асинхронни методи в протоколите, може да посочите с маркер isolated, че те трябва да се викат на конкретен actor.

Резюме

  • Actor — референтен тип с автоматична изолация на данни, представен в Swift 5.5+
  • Изолация работи чрез изпълнителя на actor, който обработва заявките последователно
  • Външен достъп до actor изисква await — това гарантира липсата на състезания
  • Nonisolated позволява да декларирате синхронни методи без достъп до изолираното състояние
  • Глобални actor (@MainActor) разпространяват изолацията върху цялата нишка на изпълнение
  • Reentrancy предотвратява deadlock, позволявайки повторно вхождане в actor по време на await

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също