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

Ответ

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

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

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

    • Серийная очередь: Гарантирует последовательный доступ.
      
      let serialQueue = DispatchQueue(label: "com.example.serial")
      var array = [Int]()

    func addElement(_ element: Int) { serialQueue.async { array.append(element) } }

    *   **Concurrent очередь с барьером:** Позволяет параллельные чтения, но записи выполняются эксклюзивно.
    ```swift
    let concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)
    var dictionary = [String: Int]()
    
    func setValue(_ value: Int, for key: String) {
        concurrentQueue.async(flags: .barrier) {
            dictionary[key] = value
        }
    }
  2. Использование примитивов синхронизации: NSLock, os_unfair_lock.

  3. Использование Actor (Swift 5.5+): Наиболее современный и рекомендуемый способ.

    actor SafeCounter {
        private var count = 0
    
        func increment() {
            count += 1
        }
    
        func getCount() -> Int {
            return count
        }
    }

Примечание: NSArray, NSDictionary в их неизменяемой (immutable) форме потокобезопасны, так как их состояние не может быть изменено после создания.

Ответ 18+ 🔞

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

Так как же не обосраться с потоками?

  1. Великий и ужасный GCD (Grand Central Dispatch):

    • Серийная очередь. Всё по очереди, как в совковой столовой. Зато надёжно, ёпта.
      
      let serialQueue = DispatchQueue(label: "com.example.serial")
      var array = [Int]()

    func addElement(_ element: Int) { serialQueue.async { array.append(element) // Сиди, жди своей очереди, не дергайся. } }

    *   **Concurrent очередь с барьером.** Это поумнее. Читать могут все сразу, а вот запись — только одна, и все на это время встают раком.
    ```swift
    let concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)
    var dictionary = [String: Int]()
    
    func setValue(_ value: Int, for key: String) {
        concurrentQueue.async(flags: .barrier) {
            dictionary[key] = value // Все, стопэ, записываю я! Остальные — пошли нахуй, подождите.
        }
    }
  2. Старые добрые замки (NSLock, os_unfair_lock). Классика жанра. Взял замок — делай что хочешь, отпустил — пусть другие мучаются. Главное — не забудь отпустить, а то deadlock тебе в сраку.

  3. Акторы (Actor), Swift 5.5+. Это, блядь, новое слово в технике. Модно, стильно, молодёжно. Компилятор сам за тебя всё проверит, волнение ебать ноль.

    actor SafeCounter {
        private var count = 0
    
        func increment() {
            count += 1 // Только я, красавец, могу здесь это делать. Посторонним вход воспрещён.
        }
    
        func getCount() -> Int {
            return count
        }
    }

Важное примечание, чтоб ты не обосрался: Есть ещё старые обезьянки из Objective-C — NSArray, NSDictionary. Если ты их создал неизменяемыми (immutable), то можешь расслабиться. Они потокобезопасны, потому что их, грубо говоря, нельзя ебать после создания. Состояние фиксированное — проблем нет. Но как только начинается изменение — всё, пиши пропало, правила те же.