использование диспетчерской группы в мульти для цикла с задачами urlsession - PullRequest
1 голос
/ 10 октября 2019

Я использую группу диспетчеризации wait(), которая блокирует мой цикл for от завершения кода до завершения набора задач urlsession (в другом цикле с обработчиком завершения) перед добавлением нового элемента в мой массив

текущий код завершит первый цикл до завершения второго цикла urlClass.selectfoodURL

Я хочу добавить массив в историю приемов после того, как мой urlfood for loop завершил проблемув моем подходе к использованию групп рассылки - wait(), когда выбранная еда называется застрявшей urlsession и не заканчивается group.wait

func userSnackHistoryArray() {
    let group = DispatchGroup()
    let Arrays // array of dictionary
    for array in Arrays {

        var generateMeal = MealDetails() // struct type
        do {
            let aa = try JSONDecoder().decode(userSnack.self, from: array)
            generateMeal.names = convertToJsonFile.type

            for name in generateMeal.names!{
                group.enter()

                urlClass.selectfoodURL(foodName: name){ success in
                    generateMeal.units!.append(allVariables.selectedUnit)
                    group.leave()
                }
            }
            // my select food is called but the urlsession stuck and doesnt complete with group.wait is active
            // group.wait()
            mealHistory.append(generateMeal)
        } catch { }
    }

    group.notify(queue: .main){
        print("complete")
    }
}

Я сократил код, чтобы сосредоточиться напроблема, я могу разделить свой код на две функции и решить проблему, но я хочу использовать только одну функцию, какие-либо предложения или идеи?

1 Ответ

0 голосов
/ 31 октября 2019

Вместо ожидания вы должны просто создать локальный массив значений для добавления, а затем добавить их, когда это будет сделано:

func retrieveSnacks() {
    var snacksToAdd: [Snack] = []

    let group = DispatchGroup()

    ...

    for url in urls {
        group.enter()
        fetchSnack(with: url) { result in
            dispatchPrecondition(condition: .onQueue(.main))   // note, I’m assuming that this closure is running on the main queue; if not, dispatch this appending of snacks (and `leave` call) to the main queue

            if case .success(let snack) = result {
                snacksToAdd.append(snack)
            }

            group.leave()
        }
    }

    // when all the `leave` calls are called, only then append the results

    group.notify(queue: .main) {
        self.snacks += snacksToAdd

        // trigger UI update, or whatever, here
    }
}

Обратите внимание, что приведенное выше не гарантирует, что объекты будут добавлены вОригинальный заказ. Если вам это нужно, вы можете использовать словарь для построения временных результатов и затем добавлять результаты в отсортированном порядке:

func retrieveSnacks() {
    var snacksToAdd: [URL: Snack] = [:]

    let group = DispatchGroup()

    ...

    for url in urls {
        group.enter()
        fetchSnack(with: url) { result in
            if case .success(let snack) = result {
                snacksToAdd[url] = snack
            }
            group.leave()
        }
    }

    group.notify(queue: .main) {
        let sortedSnacks = urls.compactMap { snacksToAdd[$0] }
        self.snacks += sortedSnacks

        // trigger UI update, or whatever, here
    }
}

Наконец, я мог бы предложить принять шаблон обработчика завершения:

func retrieveSnacks(completion: @escaping ([Snack]) -> Void) {
    var snacksToAdd: [URL: Snack] = [:]

    let group = DispatchGroup()

    ...

    for url in urls {
        group.enter()
        fetchSnack(with: url) { result in
            if case .success(let snack) = result {
                snacksToAdd[url] = snack
            }
            group.leave()
        }
    }

    group.notify(queue: .main) {
        let sortedSnacks = urls.compactMap { snacksToAdd[$0] }
        completion(sortedSnacks)
    }
}

retrieveSnacks { addedSnacks in
    self.snacks += addedSnacks
    // update UI here
}

Этот шаблон гарантирует, что вы не перепутаете свой сетевой код с вашим кодом пользовательского интерфейса.


Я извиняюсь, что вышеприведенное несколько отреагировало из вашего фрагмента кода, но этого было недостаточнотам для меня, чтобы проиллюстрировать, как именно это будет выглядеть. Но, надеюсь, вышеприведенный пример иллюстрирует шаблон, и вы можете увидеть, как вы примените его к своей кодовой базе. Таким образом, не заблудитесь в деталях, но сосредоточьтесь на базовом шаблоне построения записей, которые нужно добавить в локальную переменную, и обновите только окончательные результаты в блоке .notify.

FWIW, этосигнатура метода для метода, который используются вышеупомянутыми фрагментами для асинхронного извлечения рассматриваемых объектов.

func fetchSnack(with url: URL, completion: @escaping (Result<Snack, Error>) -> Void) {
    ...

    // if async fetch not successful 

    DispatchQueue.main.async {
        completion(.failure(error))
    }

    // if successful

    DispatchQueue.main.async {
        completion(.success(snack))
    }
}
...