Как разделить функции для чтения и записи значений
Что проверяют
За этой формулировкой обычно стоит задача о потокобезопасности: как дать много параллельных чтений и эксклюзивную запись в общее значение. Иногда вопрос шире, про разграничение доступа: кто вообще может менять данные. Следом почти всегда просят показать код и объяснить, где может быть гонка.
Как отвечать
Начните с API, потом переходите к потокам.
Разграничение доступа: private(set) var оставляет запись внутри типа, а наружу отдают протокол только с геттером. Потребитель, которому нужно лишь читать, получает узкий протокол и не может случайно изменить состояние.
Потокобезопасность, от классики к современному:
- concurrent-очередь с барьером: своя
DispatchQueue(label: "cache", attributes: .concurrent), чтение черезqueue.sync { storage[key] }, запись черезqueue.async(flags: .barrier) { ... }. Чтения идут параллельно, барьерная запись ждёт их окончания и выполняется одна; - блокировки:
NSLock,OSAllocatedUnfairLockс iOS 16,Mutexиз фреймворка Synchronization с iOS 18. Проще барьера, но чтения тоже становятся взаимоисключающими, что для коротких операций обычно не проблема; actor: компилятор сам сериализует доступ к состоянию, обращения снаружи идут черезawait. Параллельных чтений нет, зато в Swift 6 со строгой проверкой конкурентности это самый предсказуемый вариант.
Выбор обосновывайте нагрузкой: много чтений и редкие записи хорошо ложатся на барьер, код на async/await лучше дружит с актором.
Где спотыкаются
Ставят барьер на DispatchQueue.global(). На глобальных очередях флаг .barrier игнорируется, и запись идёт наравне с чтениями.
Считают код безопасным, раз безопасны отдельные чтение и запись. value = value + 1 это два вызова, и между ними влезает другой поток. Нужен один метод, который меняет значение внутри барьера или актора.
Вызывают queue.sync изнутри блока, который уже выполняется на той же очереди, и получают дедлок.