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()
,它阻止我的afor
循环完成代码,直到一组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(零食))
}
}