Как обеспечивается потокобезопасность у value и reference типов в Swift?
Что проверяют
Понимаете ли вы, что семантика значений убирает общее изменяемое состояние, но сама по себе не делает код потокобезопасным. Следом почти всегда спрашивают про акторы, Sendable и то, как строгая проверка конкурентности в Swift 6 ловит гонки на этапе компиляции.
Как отвечать
Про value-типы (struct, enum, а также Array, Dictionary, String):
- своя копия у каждого потока: независимые копии друг другу не мешают. Copy-on-write у стандартных коллекций копирует буфер при первой записи, и для независимых копий это безопасно;
- одна переменная на всех: если два потока меняют одну и ту же
var, будь то свойство класса, глобальная переменная или значение, захваченное замыканием, это гонка данных, хоть тип и значимый.appendв один массив из двух очередей может уронить приложение; - ссылки внутри: структура, в которой лежит экземпляр класса, копирует только ссылку.
Reference-типы (class) защищают явно:
- последовательная очередь: все обращения идут через одну
DispatchQueue, а для схемы «много читателей, один писатель» concurrent-очередь сasync(flags: .barrier)на запись; - блокировки:
NSLock,OSAllocatedUnfairLock(iOS 16+),Mutexиз модуляSynchronization(iOS 18+); - акторы:
actorизолирует своё состояние, снаружи к нему обращаются только черезawait. Для интерфейса есть@MainActor.
Дальше про Sendable. Структуры и перечисления из Sendable-полей получают соответствие автоматически, класс может быть Sendable, только если он final и хранит лишь неизменяемые let-свойства Sendable-типов, акторы Sendable всегда. В языковом режиме Swift 6 передача не-Sendable значения между доменами изоляции становится ошибкой компиляции.
Где спотыкаются
Говорят «структуры потокобезопасны». Безопасны независимые копии, а общая изменяемая переменная остаётся гонкой.
Забывают про реентерабельность акторов. Пока метод актора ждёт на await, может выполниться другой вызов, и проверка, сделанная до await, после него уже неверна.
Глушат компилятор через @unchecked Sendable или nonisolated(unsafe) без синхронизации внутри. Ошибка пропадает, гонка остаётся.