Как исправить порядок результатов, чтобы он совпадал с порядком входных данных
Что проверяют
Понимаете ли вы, откуда берётся перепутанный порядок: почти всегда это параллельная обработка, где результаты складываются в порядке завершения, а не запуска. Следом обычно спрашивают, как сохранить параллельность и не получить гонку при записи в общий массив.
Как отвечать
Сначала назовите причину. Код вида «запустить по задаче на элемент и в колбэке сделать results.append» с DispatchGroup или TaskGroup отдаёт результаты в том порядке, в каком закончились запросы. Если колбэки к тому же приходят на разных потоках, это ещё и гонка на массиве.
Лечится одним приёмом: каждый результат несёт индекс исходного элемента.
withTaskGroup: дочерняя задача возвращает пару(index, value), родитель в циклеfor awaitкладёт значение в заранее созданный массивArray<Output?>(repeating: nil, count: input.count)по индексу. Сборка идёт в родительской задаче, общего изменяемого состояния нет, и строгая проверка конкурентности Swift 6 не ругается;- сортировка после сбора: собрать пары и сделать
sorted { $0.index < $1.index }. Работает, но это O(n log n) вместо O(n) у записи по индексу; async let: если элементов фиксированное небольшое число, порядок задаёт сам код;- последовательный
forсawait: порядок сохраняется сам, но пропадает параллельность. Честный вариант для пяти элементов, а не для пятисот; DispatchQueue.concurrentPerform: запись по индексу в заранее выделенный буфер, но доступ к нему нужно защитить.
Хорошо упомянуть ограничение параллельности: запускать сразу тысячу задач незачем, поэтому в группу добавляют новую задачу по мере завершения предыдущей и держат одновременно, скажем, не больше восьми.
Где спотыкаются
Сортируют результаты по значению, а не по исходному индексу. На уникальных возрастающих числах это случайно работает, на реальных данных нет.
Собирают результаты в Dictionary или Set и потом обходят их. Порядок обхода словаря в Swift не определён, а Set к тому же теряет дубли.
Пишут в общий массив из нескольких задач без синхронизации. В режиме Swift 6 компилятор такое часто не пропускает, а в старом режиме приложение падает раз в сотню запусков.