【问题标题】:using dispatch group in multi for loop with urlsession tasks在多 for 循环中使用调度组和 urlsession 任务
【发布时间】:2020-02-07 16:29:10
【问题描述】:

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

当前代码将在urlClass.selectfoodURL的第二个循环完成之前完成第一个循环

我想在我的 urlfood for 循环完成后将数组附加到膳食历史中 我使用调度组的方法中的一个问题是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")
    }
}

我已缩短代码以专注于问题,我可以将代码拆分为两个函数并解决问题,但我只想使用一个函数 有什么建议或想法吗?

【问题讨论】:

    标签: swift httprequest grand-central-dispatch swift5 urlsession


    【解决方案1】:

    而不是等待,您应该只创建一个要添加的值的本地数组,然后在完成后添加它们:

    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 代码纠缠在一起。


    我很抱歉,上面的代码在某种程度上是从您的代码 sn-p 中重构的,但那里还不足以让我说明它的确切外观。但希望以上说明了该模式,您可以看到如何将其应用到您的代码库中。所以,不要迷失在细节中,而是关注构建要添加到局部变量中的记录的基本模式,并且只更新 .notify 块中的最终结果。

    FWIW,这是上述 sn-ps 用于异步获取相关对象的方法的方法签名。

    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))
        }
    }
    

    【讨论】:

    • 谢谢!这绝对是救命稻草。我一直在努力解决如何干净地设置DispatchGroup 调用,这很棒。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多