Как происходит доступ к данным вне акторов в Swift?
Что проверяют
Понимаете ли вы изоляцию акторов как правило компилятора, а не как «замок вокруг объекта». Следом обычно спрашивают про Sendable, про реентерабельность и про то, почему после await внутри актора нельзя доверять прочитанному раньше состоянию.
Как отвечать
Изменяемое состояние актора изолировано: код снаружи не может прочитать или изменить его синхронно. Отсюда правила доступа:
- чтение и вызовы снаружи: только асинхронно, через
await. Это точка приостановки, вызов ставится в очередь актора, и в каждый момент на актор выполняется одна задача; - изменение свойств: напрямую снаружи запрещено даже с
await,counter.value = 1не скомпилируется. Состояние меняют методами самого актора; nonisolated: метод или вычисляемое свойство, которые не трогают изолированное состояние, можно пометитьnonisolatedи вызывать синхронно, например дляdescriptionили констант;let-свойства: неизменяемые свойстваSendable-типа внутри модуля читаются безawait, гонки там быть не может;- граница изоляции: всё, что передаётся в актор и возвращается из него, должно быть
Sendable. Класс с изменяемым состоянием просто так через границу не пройдёт, в режиме Swift 6 это ошибка компиляции, а не предупреждение; @MainActor: глобальный актор для интерфейса. Данные из фонового актора возвращаются черезawaitв код, изолированный наMainActor, и уже там обновляют UI.
Отдельно скажите про реентерабельность: пока метод актора ждёт на await, актор выполняет другие вызовы. Поэтому проверку вроде «есть ли уже в кэше» повторяют после приостановки или хранят в кэше саму Task, чтобы два параллельных запроса не ушли в сеть дважды.
Где спотыкаются
Говорят, что актор это отдельный поток. Актор гарантирует последовательный доступ, а исполняется на общем пуле потоков, и между await задача может сменить поток.
Обходят изоляцию через глобальные переменные или nonisolated(unsafe), чтобы заглушить компилятор. Гонка при этом никуда не девается.
Держат синхронные ожидания внутри актора: семафор или DispatchGroup.wait() блокируют поток кооперативного пула и могут подвесить всё приложение.