Как использовать DispatchGroup для ожидания завершения группы асинхронных задач в iOS
Что проверяют
Понимаете ли вы, как устроен счётчик DispatchGroup и чем notify отличается от wait. Следом обычно спрашивают, как собрать результаты и ошибки всех запросов и как та же задача решается в Swift Concurrency.
Как отвечать
DispatchGroup это счётчик незавершённых задач: когда он доходит до нуля, группа считается завершённой.
enter()иleave(): ручное управление счётчиком для асинхронных API с колбэком.enter()вызывается до старта запроса,leave()в его колбэке, ровно по одному разу. Надёжнее всего писатьdefer { group.leave() }первой строкой колбэка, тогда раннийreturnне собьёт баланс;queue.async(group:): группа сама отслеживает блок, но только до его возврата. Если блок лишь запускает сетевой запрос, группа посчитает работу сделанной раньше времени;notify(queue:): колбэк, который выполнится, когда счётчик обнулится. Поток не блокирует, для обновления интерфейса передают.main;wait()иwait(timeout:): блокируют текущий поток, вторая форма возвращает.successили.timedOut. Уместны в фоновом коде и тестах, но не на главном потоке.
Сама группа ничего не знает о результатах. Колбэки приходят на разных потоках, поэтому ответы складывают в общий массив или словарь под синхронизацией, а ошибки собирают отдельно.
В новом коде ту же задачу решают async let для фиксированного числа запросов и withThrowingTaskGroup для списка. Они возвращают значения, пробрасывают ошибку и поддерживают отмену, чего у DispatchGroup нет.
Где спотыкаются
Теряют баланс вызовов. Лишний leave() роняет приложение, а пропущенный в ветке с ошибкой означает, что notify не сработает никогда.
Вызывают wait() на главном потоке. Интерфейс замирает, а если колбэки запросов тоже доставляются на main, получается дедлок.
Пишут результаты из колбэков в обычный Array без синхронизации. Гонка проявится редко и в виде случайного падения, которое трудно воспроизвести.