← Назад к вопросам

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

2.0 Middle🔥 72 комментариев
#Многопоточность и асинхронность#Язык Swift

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Потокобезопасность классов в iOS разработке

В iOS разработке классы не являются потокобезопасными по умолчанию. Это фундаментальное правило, которое каждый разработчик должен четко понимать при работе с многопоточностью. В подавляющем большинстве случаев стандартные классы Foundation и UIKit не гарантируют безопасность при одновременном доступе из нескольких потоков.

Почему классы не потокобезопасны по умолчанию?

  1. Производительность: Гарантия потокобезопасности требует дополнительных механизмов синхронизации (блокировки, семафоры, атомарные операции), которые снижают производительность. Поскольку большинство объектов используется в однопоточном контексте, Apple оптимизирует их для скорости.

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

Пример потоконебезопасного класса

Рассмотрим стандартный NSMutableArray:

class ThreadUnsafeExample {
    private var items: [String] = []
    
    func addItem(_ item: String) {
        items.append(item) // ОПАСНО: несинхронизированный доступ
    }
    
    func removeItem(at index: Int) -> String? {
        guard index < items.count else { return nil }
        return items.remove(at: index) // ОПАСНО: возможен краш
    }
}

Если два потока одновременно вызовут addItem() или один поток будет читать, а другой удалять элементы, это может привести к:

  • Data races (гонкам данных)
  • Несогласованному состоянию
  • Крашам приложения

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

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

class ThreadSafeQueue {
    private var items: [String] = []
    private let queue = DispatchQueue(label: "com.example.threadsafe.queue", 
                                      attributes: .concurrent)
    
    func addItem(_ item: String) {
        queue.async(flags: .barrier) {
            self.items.append(item)
        }
    }
    
    func getItems() -> [String] {
        return queue.sync {
            return self.items
        }
    }
}

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

class ThreadSafeCounter {
    private var count: Int = 0
    private let lock = NSLock()
    
    func increment() {
        lock.lock()
        defer { lock.unlock() }
        count += 1
    }
    
    func getCount() -> Int {
        lock.lock()
        defer { lock.unlock() }
        return count
    }
}

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

actor BankAccount {
    private var balance: Double = 0
    
    func deposit(amount: Double) {
        balance += amount
    }
    
    func withdraw(amount: Double) -> Bool {
        if balance >= amount {
            balance -= amount
            return true
        }
        return false
    }
    
    func getBalance() -> Double {
        return balance
    }
}

Исключения: потокобезопасные классы

Некоторые классы в iOS являются потокобезопасными:

  • DispatchQueue и другие примитивы GCD
  • NSOperationQueue (при правильном использовании)
  • NSCache (частично потокобезопасен)
  • Атомарные свойства с @Atomic (используя os_unfair_lock или pthread_mutex)

Практические рекомендации

  1. Документируйте потокобезопасность ваших классов в комментариях
  2. Используйте value types (struct, enum) там, где возможно - они безопаснее при копировании
  3. Изолируйте mutable state - минимизируйте изменяемое состояние, которое доступно из нескольких потоков
  4. Тестируйте многопоточность с помощью инструментов вроде Thread Sanitizer
  5. Избегайте преждевременной оптимизации - добавляйте синхронизацию только при необходимости

Ключевой вывод: Всегда предполагайте, что класс не потокобезопасен, если это явно не задокументировано. Ответственность за обеспечение корректного доступа из нескольких потоков лежит на разработчике, использующем класс. Современные подходы (акторы, async/await) значительно упрощают создание потокобезопасного кода, но понимание базовых принципов остается критически важным.

Являются ли классы потокобезопасными? | PrepBro