Какие проблемы конкурентного доступа решает Actor в Swift?

«Какие проблемы конкурентного доступа решает Actor в Swift?» — вопрос из категории Многопоточность, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Actor решает проблему гонок данных (data races) при работе с изменяемым состоянием в параллельном коде.

Ключевые решаемые проблемы:

  1. Data Races: Гарантирует, что доступ к изолированному (isolated) состоянию актора происходит последовательно. В один момент времени только одна задача может исполнять код, работающий с его свойствами.
  2. Упрощение синхронизации: Избавляет разработчика от необходимости вручную использовать примитивы синхронизации (DispatchQueue, NSLock, семафоры), которые подвержены ошибкам (deadlocks, priority inversion).
  3. Безопасность на уровне компилятора: Компилятор Swift статически проверяет, что код вне актора не обращается к его изолированным свойствам напрямую, вынуждая использовать await для взаимодействия.

Принцип работы: Actor изолирует свои изменяемые свойства. Все его методы и свойства, помеченные как nonisolated, считаются безопасными для параллельного доступа. Для вызова изолированного метода извне требуется await, что указывает на потенциальную приостановку текущей задачи до возможности безопасного доступа.

Пример:

actor BankAccount {
    private(set) var balance: Double = 0.0 // Изолированное состояние

    func deposit(amount: Double) {
        balance += amount
    }

    func withdraw(amount: Double) -> Bool {
        guard balance >= amount else { return false }
        balance -= amount
        return true
    }
}

// Использование
Task {
    let account = BankAccount()
    await account.deposit(amount: 100) // Доступ сериализован
    let success = await account.withdraw(amount: 50)
    print("Withdrawal successful:", success)
}

Важно: Actor защищает от гонок данных, но не от всех проблем параллелизма (например, race conditions на логическом уровне или голодание).