Является ли lazy-свойство в Swift потокобезопасным?

«Является ли lazy-свойство в Swift потокобезопасным?» — вопрос из категории Swift Core, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Нет, ленивая инициализация (lazy) в Swift по умолчанию не является потокобезопасной.

Проблема: Если несколько потоков одновременно обратятся к еще не инициализированному lazy-свойству, может произойти состояние гонки (race condition). Это может привести к тому, что инициализирующее замыкание выполнится более одного раза, а конечное значение будет неопределенным.

Пример небезопасного кода:

class DataManager {
    // Не потокобезопасное lazy-свойство
    lazy var config: [String: String] = {
        // Длительная операция...
        return ["key": "value"]
    }()
}
// Одновременный доступ из нескольких потоков -> риск.

Как обеспечить потокобезопасность?

  1. Инициализация до параллельного доступа: Гарантировать, что свойство будет инициализировано в главном потоке до его использования в других.
  2. Использование мьютекса или очереди: Вручную синхронизировать доступ.

    import Dispatch
    
    class DataManager {
        private let queue = DispatchQueue(label: "com.example.sync")
        private var _config: [String: String]?
    
        var config: [String: String] {
            queue.sync {
                if let value = _config { return value }
                let newValue = ["key": "value"] // Инициализация
                _config = newValue
                return newValue
            }
        }
    }
  3. Property Wrapper (Swift 5.1+): Создать или использовать готовый обертку (например, @Atomic), которая инкапсулирует логику синхронизации.

Вывод: При использовании lazy в многопоточной среде необходимо самостоятельно добавлять механизмы синхронизации.