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 напоминает класс, но поведение radically отличается.

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)
// Async call — required from outside actor
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()

Каждый вызов метода actor требует await, потому что actor может быть занят другой задачей. Это — не баг, а сознательный дизайн, предотвращающий гонки. Swift неявно делает геттеры и сеттеры свойств actor async, поэтому чтение свойства тоже требует await.

Основные отличия Actor от класса

Actor и класс являются ссылочными типами, но их поведение в многопоточной среде кардинально различается. Класс не обеспечивает никакой автоматической защиты от гонок, тогда как actor встраивает её на уровне компилятора через систему типов.

ХарактеристикаActorClass
Защита от гонокАвтоматическая, на уровне компилятораТребует ручной синхронизации
Доступ к свойствамТолько через await извнеПрямой, без синхронизации
НаследованиеТолько от других actorСтандартное классовое
Protocol conformanceМожет conformиться протоколамСтандартное
PerformanceНизкий overhead при изоляцииБез синхронизации — быстрее

Наследование 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() {
        // This code is guaranteed to run on the main thread
        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

Когда код внутри actor выполняет await, actor приостанавливает текущую задачу и может обработать другую, поставленную в очередь. После завершения 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 — reentrancy point
        let result = await fetchData(id: id)
        // Cache may have changed after await — recheck
        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-свойство, которое возвращает Snapshot состояния actor в виде Sendable-структуры.

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

Да, actor может соответствовать протоколам. Если протокол содержит изолированные требования (actor-isolated), они автоматически становятся actor-изолированными. Для асинхронных методов в протоколах можно указать, что они должны вызываться на конкретном actor с помощью маркера isolated.

Итоги

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

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

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также