Как обеспечить потокобезопасность (thread safety) для переменной в Swift?

«Как обеспечить потокобезопасность (thread safety) для переменной в Swift?» — вопрос из категории Многопоточность, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Потокобезопасность гарантирует корректность данных при одновременном доступе из нескольких потоков. Вот основные подходы в Swift:

1. Использование DispatchQueue с барьером (barrier) Наиболее распространённый и гибкий способ для чтения/записи.

class ThreadSafeContainer<T> {
    private var value: T
    private let queue = DispatchQueue(label: "com.example.threadsafe", attributes: .concurrent)

    init(value: T) { self.value = value }

    var currentValue: T {
        get {
            return queue.sync { value } // Конкурентное чтение
        }
        set {
            queue.async(flags: .barrier) { // Эксклюзивная запись
                self.value = newValue
            }
        }
    }
}

2. Использование мьютексов (Mutex) Более низкоуровневый, но эффективный контроль.

import os

class ThreadSafeValue<T> {
    private var value: T
    private let lock = os_unfair_lock()

    init(value: T) { self.value = value }

    func update(_ block: (inout T) -> Void) {
        os_unfair_lock_lock(&lock)
        defer { os_unfair_lock_unlock(&lock) }
        block(&value)
    }

    func read() -> T {
        os_unfair_lock_lock(&lock)
        defer { os_unfair_lock_unlock(&lock) }
        return value
    }
}

3. Property Wrapper (обёртка свойства) Удобный современный способ инкапсулировать логику синхронизации.

@propertyWrapper
struct ThreadSafe<T> {
    private var value: T
    private let lock = NSLock()

    init(wrappedValue: T) {
        self.value = wrappedValue
    }

    var wrappedValue: T {
        get {
            lock.lock()
            defer { lock.unlock() }
            return value
        }
        set {
            lock.lock()
            defer { lock.unlock() }
            value = newValue
        }
    }
}

// Использование
class Model {
    @ThreadSafe var counter: Int = 0
}

Важно: Потокобезопасность переменной не гарантирует потокобезопасность всей системы. Необходимо анализировать все точки доступа к общим данным.