Как вы применяете многопоточность (concurrency) в iOS?

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

Ответ

В iOS многопоточность обеспечивается системами Grand Central Dispatch (GCD), OperationQueue и, начиная с Swift 5.5, нативными async/await и Actors.

1. Grand Central Dispatch (GCD): Фреймворк низкого уровня для управления очередями задач (очередями отправки — DispatchQueue).

  • Типы очередей:
    • DispatchQueue.main — последовательная (serial) очередь для обновления UI.
    • Глобальные concurrent очереди с приоритетами QoS (.userInteractive, .userInitiated, .utility, .background).
    • Собственные serial или concurrent очереди.
      // Выполнение тяжелой задачи в фоне и возврат на main queue
      DispatchQueue.global(qos: .userInitiated).async {
      let processedData = self.processImage(data)
      DispatchQueue.main.async {
      self.imageView.image = processedData
      }
      }
  • Синхронизация: DispatchGroup для ожидания завершения группы задач, барьеры (barrier) для безопасной записи в concurrent очереди, семафоры (DispatchSemaphore).

2. OperationQueue: Абстракция更高 уровня над GCD, построенная на классах Operation. Позволяет:

  • Создавать зависимые операции.
  • Устанавливать приоритеты (queuePriority).
  • Отменять операции (operation.cancel()).
  • Ограничивать количество параллельных операций (maxConcurrentOperationCount).

3. Swift Concurrency (async/await): Современный, безопасный и читаемый подход.

func fetchData() async throws -> Data {
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

// Вызов в Task
Task {
    do {
        let data = try await fetchData()
        await MainActor.run { self.data = data } // Безопасное обновление UI
    } catch {
        // Обработка ошибки
    }
}
  • Actors: Решают проблему гонки данных (data race) путем изоляции состояния. Доступ к свойствам actor происходит последовательно.

Best Practice: Всегда обновляйте UI только на главном потоке. Для CPU-интенсивных задач используйте фоновые очереди с подходящим QoS. Избегайте блокировок (deadlock), особенно на DispatchQueue.main. Для новых проектов предпочтительнее использовать Swift Concurrency.