С какими проблемами многопоточности в iOS вы сталкивались и как их решали?

«С какими проблемами многопоточности в iOS вы сталкивались и как их решали?» — вопрос из категории Многопоточность, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Основные проблемы: состояние гонки (race condition), взаимная блокировка (deadlock) и инверсия приоритетов.

1. Состояние гонки (Race Condition)

Возникает, когда несколько потоков одновременно обращаются к изменяемым общим данным.

Пример на Swift:

var counter = 0 // Небезопасная общая переменная
DispatchQueue.concurrentPerform(iterations: 100) { _ in
    counter += 1 // Неатомарная операция чтение-изменение-запись
}
// Результат `counter` может быть меньше 100

Решения:

  • Серийная очередь (Serial DispatchQueue): Гарантирует последовательный доступ.
    let serialQueue = DispatchQueue(label: "com.example.serial")
    serialQueue.async { counter += 1 }
  • Изоляция актором (Actor): Начиная с Swift 5.5.
    actor Counter {
        private var value = 0
        func increment() { value += 1 }
    }
  • Атомарные операции или примитивы синхронизации: NSLock, os_unfair_lock, DispatchSemaphore.

2. Взаимная блокировка (Deadlock)

Пример классического deadlock в iOS:

let mainQueue = DispatchQueue.main
mainQueue.sync {
    // Этот блок никогда не выполнится, так как
    // он ждёт освобождения main queue, которая в данный момент
    // заблокирована этим же вызовом `sync`.
    mainQueue.sync { print("Deadlock") }
}

Как избежать: Не вызывать sync на текущей очереди, особенно на main. Использовать асинхронные вызовы (async) или перепроектировать поток данных.

Основные инструменты: DispatchQueue, OperationQueue с зависимостями, Акторы (Swift Concurrency).