Являются ли стандартные коллекции Swift потокобезопасными?

Ответ

Нет. Стандартные коллекции Swift (Array, Dictionary, Set) не являются потокобезопасными (not thread-safe). Одновременный доступ на запись или одновременный доступ на чтение и запись из разных потоков приводит к гонкам данных (data races) и неопределённому поведению, включая краши.

Способы обеспечения потокобезопасности:

1. Использование GCD (Grand Central Dispatch) с барьером

Идеально для создания синхронизированной обёртки.

class ThreadSafeArray<Element> {
    private var array = [Element]()
    private let queue = DispatchQueue(label: "com.example.threadSafeArray", attributes: .concurrent)

    func append(_ element: Element) {
        queue.async(flags: .barrier) { // Барьер для записи
            self.array.append(element)
        }
    }

    var first: Element? {
        var result: Element?
        queue.sync { // Без барьера для чтения
            result = array.first
        }
        return result
    }
}

2. Использование NSLock или NSRecursiveLock

Простой подход для низкоуровневой синхронизации.

class LockedDictionary<Key: Hashable, Value> {
    private var dict = [Key: Value]()
    private let lock = NSLock()

    func set(value: Value, for key: Key) {
        lock.lock()
        defer { lock.unlock() }
        dict[key] = value
    }
}

3. Использование акторов (Actors, Swift 5.5+)

Наиболее современный и безопасный способ, гарантированный компилятором.

actor SafeCollection {
    private var items = [String]()

    func add(_ item: String) {
        items.append(item)
    }

    func getAll() -> [String] {
        return items
    }
}
// Использование: await safeCollection.add("item")

Рекомендация: Для новых проектов на Swift 5.5+ предпочтительно использовать акторы. Для поддержки legacy-кода — GCD с барьерами или locks.

Ответ 18+ 🔞

А, слушай, вот этот вопрос про коллекции в Swift — он как гнойник на заднице, блядь, который рано или поздно придётся вскрывать. Все эти твои Array, Dictionary — они же, сука, как голые младенцы в толпе пьяных матросов, если их в несколько потоков пинать начинаешь. Один пишет, другой читает — и пиздец, приложение крашнется так, что мало не покажется, в рот меня чих-пых!

Ну ладно, не буду тебя пугать, как бабушка в подъезде. Давай по делу, как взрослые люди, блядь.

Способы, чтобы эту шалаву — многопоточность — приструнить:

1. GCD с этим самым... барьером, блядь!

Это как поставить здорового мужика с дубиной у входа в сортир. Хочешь пописАть — иди, хуле. Хочешь насрать — все, блядь, ждут, пока ты свой процесс завершишь. Никто не вломится в момент свершения.

class ThreadSafeArray<Element> {
    private var array = [Element]()
    private let queue = DispatchQueue(label: "com.example.threadSafeArray", attributes: .concurrent)

    func append(_ element: Element) {
        queue.async(flags: .barrier) { // Вот этот барьер — он для писателей, блядь
            self.array.append(element)
        }
    }

    var first: Element? {
        var result: Element?
        queue.sync { // А тут читатели — пускай себе читают, падлы
            result = array.first
        }
        return result
    }
}

2. NSLock — классика, ёпта!

Прям как старый дедовский амбарный замок. Пришёл, щёлк — закрыл, сделал свои делишки, щёлк — открыл. Проще некуда, но если забудешь открыть — все, пидарас, deadlock, и все будут стоять и ждать, пока ты сдохнешь.

class LockedDictionary<Key: Hashable, Value> {
    private var dict = [Key: Value]()
    private let lock = NSLock() // Вот он, красавец, железяка!

    func set(value: Value, for key: Key) {
        lock.lock()
        defer { lock.unlock() } // Это, блядь, гениально — как бы ты ни выпрыгнул, замок откроется
        dict[key] = value
    }
}

3. Акторы (Swift 5.5+) — это, сука, будущее наступило!

Это уже не замок, это целая система безопасности уровня президента. Компилятор сам, блядь, следит, чтобы к твоим данным в один момент времени только один поток имел доступ. Красота, ёперный театр!

actor SafeCollection { // Видишь это слово `actor`? Всё, пиздец гонкам данных!
    private var items = [String]()

    func add(_ item: String) {
        items.append(item)
    }

    func getAll() -> [String] {
        return items
    }
}
// Использовать — только с `await`, блядь. Терпения, сука, наберись.
// await safeCollection.add("item")

Итог, блядь, короткий:
Если пишешь на свежем Swift — бери акторы и не еби себе мозг.
Если ковыряешь старый код, который видел виды — GCD с барьером твой друг.
А NSLock — это как молоток: иногда без него никуда, но можно и по пальцам получить, если неаккуратно.

Вот и вся философия, блядь. Не усложняй.