Какие основные методы у DispatchGroup в Swift и для чего они используются?

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

Ответ

DispatchGroup — инструмент Grand Central Dispatch (GCD) для синхронизации выполнения группы асинхронных задач. Позволяет дождаться завершения всех задач перед выполнением финального действия.

Ключевые методы:

Метод Назначение
enter() Увеличивает внутренний счетчик группы на 1, отмечая начало новой асинхронной задачи.
leave() Уменьшает счетчик на 1, сигнализируя о завершении задачи. Должен вызываться ровно столько раз, сколько enter().
notify(queue:execute:) Задает замыкание, которое выполнится на указанной очереди, когда счетчик группы станет равным 0. Не блокирует текущий поток.
wait() Блокирует текущий поток до тех пор, пока счетчик группы не станет 0. Может принимать timeout.

Базовый шаблон использования:

let group = DispatchGroup()
let queue = DispatchQueue.global(qos: .userInitiated)

// Задача 1
group.enter()
queue.async {
    defer { group.leave() } // Гарантированный вызов leave()
    // Выполнение асинхронной работы
    downloadImage(from: url1)
}

// Задача 2
group.enter()
queue.async {
    defer { group.leave() }
    // Другая асинхронная работа
    processData()
}

// Действие после завершения ВСЕХ задач
group.notify(queue: .main) {
    // Обновление UI на главном потоке
    self.updateInterface()
    print("All tasks completed")
}

Важные нюансы:

  1. Баланс enter/leave: Несбалансированные вызовы приведут к вечному ожиданию (deadlock) или преждевременному срабатыванию notify.
  2. Использование defer: Рекомендуется вызывать leave() внутри defer в начале блока задачи, чтобы гарантировать его выполнение даже при возникновении ошибки.
  3. wait() vs notify(): wait() блокирует поток и обычно используется в фоновых очередях. notify() — неблокирующий, предпочтителен для большинства сценариев.
  4. Таймаут для wait():
    if group.wait(timeout: .now() + 5.0) == .timedOut {
        print("Tasks took too long")
    }