Что такое Actor в Swift?

Ответ

Actor — это тип данных, введенный в Swift для безопасной работы с изменяемым состоянием в конкурентном (многопоточном) коде. Он решает проблему гонок данных (data races).

Ключевой принцип: Actor изолирует свое изменяемое состояние, гарантируя, что только одна задача может получить к нему доступ в любой момент времени (взаимное исключение).

Основные особенности:

  • Изоляция состояния: Все свойства и методы по умолчанию изолированы.
  • await для доступа: Для вызова асинхронного метода или доступа к свойству actor'а извне необходимо использовать await. Это точка приостановки, где система решает, можно ли войти в actor.
  • Поддержка Sendable: Actor сам по себе является Sendable, что позволяет безопасно передавать его между потоками.

Пример: BankAccount Actor

actor BankAccount {
    private var balance: Double = 0

    func deposit(amount: Double) {
        // Внутри actor можно изменять состояние напрямую
        balance += amount
    }

    func withdraw(amount: Double) async -> Bool {
        // Проверка и изменение состояния атомарно
        if balance >= amount {
            balance -= amount
            return true
        }
        return false
    }
}

// Использование извне:
let account = BankAccount()
await account.deposit(amount: 100) // Требуется `await`
let success = await account.withdraw(amount: 50)

Почему это лучше мьютексов (locks)? Компилятор обеспечивает безопасность на этапе компиляции, предотвращая случайный доступ к состоянию без синхронизации.

Ответ 18+ 🔞

Смотри, вот эта штука — Actor в Swift — это типа как охранник в ночном клубе для твоих данных, когда там всё в многопоточном аду творится. Чтоб не было этой ебалы с гонками, когда десять потоков одновременно лезут в одну переменную как пьяные мартышки в банан.

Суть проще пареной репы: Actor запирает все свои изменяемые пожитки внутри себя и говорит: "Ребята, по одному, не толпитесь, блядь!". Одновременно к его внутренностям может лезть только одна задача. Остальные ждут своей очереди, как в хорошей поликлинике, сука.

Что там у него под капотом:

  • Изоляция, как в ковид: Все его свойства и методы по дефолту сидят в своей комнате и не высовываются.
  • Волшебное слово await: Хочешь постучаться к actor'у извне? Говори await, жди у моря погоды, пока он тебя впустит. Это точка, где твоя задача может приостановиться, пока actor занят другим делом.
  • Sendable по умолчанию: Его самого можно спокойно перекидывать между потоками — он безопасный, проверенный пацан.

Смотри, как это выглядит на практике — BankAccount:

actor BankAccount {
    private var balance: Double = 0 // Это наше святое, неприкосновенное

    func deposit(amount: Double) {
        // Внутри actor'а делаем что хотим, мы тут хозяева
        balance += amount
    }

    func withdraw(amount: Double) async -> Bool {
        // Вся проверка и списание — одним куском, атомарно. Никто не влезет посередине.
        if balance >= amount {
            balance -= amount
            return true
        }
        return false // Не хватило денег, иди работай
    }
}

// А вот как с улицы этим пользоваться:
let account = BankAccount()
await account.deposit(amount: 100) // Сказал `await` и стоишь, ждёшь, пока охранник пропустит
let success = await account.withdraw(amount: 50) // Опять эта дурацкая очередь!

И главный прикол, почему это круче старых мьютексов: С мьютексами ты сам, как обезьяна с гранатой, должен был не забыть захватить lock, а потом отпустить. А тут компилятор — он как умная бабка — стоит над душой и орет: "Куда лезешь без await, а? Щас как впишу тебе ошибку компиляции!". Безопасность на уровне языка, ёпта. Не накосячишь, даже если очень захочешь.