Swift 在带有urlsession任务的多for循环中使用调度组

Swift 在带有urlsession任务的多for循环中使用调度组,swift,httprequest,grand-central-dispatch,swift5,urlsession,Swift,Httprequest,Grand Central Dispatch,Swift5,Urlsession,我使用了一个调度组wait(),它阻止我的afor循环完成代码,直到一组urlsession任务(在另一个带有完成处理程序的循环中)在将新元素添加到数组之前完成 当前代码将在urlClass的第二个循环完成之前完成第一个循环。选择foodur 我想在urlfood for循环完成后在膳食历史记录中追加该数组 我使用调度组的方法中的一个问题是wait(),当我选择的食物被称为urlsession时,它被卡住了,并且没有与组一起完成。wait func userSnackHistoryArray()

我使用了一个调度组
wait()
,它阻止我的a
for
循环完成代码,直到一组
urlsession
任务(在另一个带有完成处理程序的循环中)在将新元素添加到数组之前完成

当前代码将在
urlClass的第二个循环完成之前完成第一个循环。选择foodur

我想在urlfood for循环完成后在膳食历史记录中追加该数组 我使用调度组的方法中的一个问题是
wait()
,当我选择的食物被称为
urlsession
时,它被卡住了,并且没有与
组一起完成。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")
    }
}
我缩短了我的代码来关注这个问题,我可以将代码分成两个函数来解决这个问题,但我只想使用一个函数
有什么建议或想法吗

不必等待,只需创建要添加的值的本地数组,然后在完成后添加它们:

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
}
此模式确保您不会将网络相关代码与UI代码纠缠在一起


很抱歉,上面的内容是从您的代码片段中重构出来的,但是没有足够的内容让我来说明它到底是什么样子。但希望上面的内容能够说明这种模式,并且您可以看到如何将其应用到代码库中。因此,不要迷失在细节中,而是关注要添加到局部变量中的构建记录的基本模式,只更新
.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))
    }
}
func fetchsnakp(带url:url,完成:@escaping(Result)->Void){
...
//如果异步获取不成功
DispatchQueue.main.async{
完成(.failure(error))
}
//如果成功
DispatchQueue.main.async{
完成(.success(零食))
}
}

不必等待,只需创建要添加的值的本地数组,然后在完成后添加它们:

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
}
此模式确保您不会将网络相关代码与UI代码纠缠在一起


很抱歉,上面的内容是从您的代码片段中重构出来的,但是没有足够的内容让我来说明它到底是什么样子。但希望上面的内容能够说明这种模式,并且您可以看到如何将其应用到代码库中。因此,不要迷失在细节中,而是关注要添加到局部变量中的构建记录的基本模式,只更新
.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))
    }
}
func fetchsnakp(带url:url,完成:@escaping(Result)->Void){
...
//如果异步获取不成功
DispatchQueue.main.async{
完成(.failure(error))
}
//如果成功
DispatchQueue.main.async{
完成(.success(零食))
}
}