Обработка асинхронных обратных вызовов в цикле for-in с DispatchGroup работает только тогда, когда все циклы выполнены успешно - PullRequest
0 голосов
/ 22 ноября 2018

Код ниже представляет собой грубый набросок задачи.Опрашивается база данных, она возвращает коллекцию результатов, эта коллекция зацикливается при поиске определенного свойства, если это свойство найдено, немедленно запрашивается хранилище файлов, а его асинхронный обработчик завершения возвращает файл в цикле.Поскольку я обрабатываю асинхронные обратные вызовы внутри цикла for-in, я использую DispatchGroup для управления этим.Эта настройка работает, только если все документы в коллекции имеют свойство someIdentifier.Если у одного документа в коллекции нет свойства, группа отправки никогда не вызывает notify(), и мы застряли в подвешенном состоянии.

someDatabaseQuery.retrieveSomeData { (data, error) in

    guard let data = data, error == nil else {
        return
    }

    // database has retrieved data, create dispatch group
    let dispatchGroup = DispatchGroup()

    for document in data { // loop through collection

        // check if document has some identifier
        guard let someIdentifier = document["someIdentifier"] as? String else {
            return
        }

        dispatchGroup.enter() // identifier found, enter dispatch

        // perform async operation inside loop
        Filestorage.getSomeFile(forURL: someIdentifier) { (data, error) in

            guard let file = data, error == nil else {
                return
            }

            // download the file
            dispatchGroup.leave() // leave dispatch

        }

    }

    dispatchGroup.notify(queue: .main) {

        // all data grabbed, load table

    }

}

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018
guard let someIdentifier = document["someIdentifier"] as? String else {
    continue // this is the proper control flow statement
}

Проблема заключалась в простом выборе неправильного оператора потока управления.Когда охранник потерпел неудачу внутри цикла, return препятствовал завершению цикла и никогда не давал группе отправки возможность уведомить.Пункт else в охране должен был иметь значение continue, которое сохраняет контроль внутри цикла (позволяя ему закончить) и, таким образом, дает группе отправки возможность уведомить.

0 голосов
/ 22 ноября 2018

Вы должны позвонить leave, если позвоните enter.Но guard внутри блока завершения getSomeFile может предотвратить вызов на leave, даже если вы позвонили enter.

Одним из решений является использование defer внутри блока завершения.Позвоните leave внутри defer, чтобы убедиться, что он вызывается независимо от того, как вы выходите из блока.

...