我有一个整数数组,我需要循环遍历它,然后发出网络请求以获取更多信息,并使用返回的信息填充我创建的新对象的数组。
我希望返回的数据按照数组提供的顺序返回,但是它以不同的顺序返回,并且假设它可能与网络请求有关。
我是开发新手,所以答案可能非常明显,但我真的陷入了下一步该做什么的死胡同。
我尝试在每个循环上为网络请求添加延迟,我尝试在数组上调用 .sort() 以确保数组保持正确的顺序
var tacticalCoverIdArray = [Int]()
var savedTacticalCoverData = [Covers]()
for coverID in tacticalCoverIdArray {
performGetRequestForSpecificCovers(coverID: coverID, targetURL: targetURL, completion: { (data, HTTPSatusCode, error) -> Void in
if HTTPSatusCode == 200 && error == nil {
do {
if coverID != 0 {
let decodedJSON = try JSONDecoder().decode([Covers].self, from: data)
savedTacticalCoverData.append(decodedJSON[0])
} else {
let data = Covers(id: 0, game: 0, image_id: "")
savedTacticalCoverData.append(data)
}
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000), execute: {
saveTacticalCoverData()
})
} catch let error {
print("Error decoding JSON: \(error)")
}
} else {
print("HTTP status code error: \(HTTPSatusCode)")
print("Error loading data: \(String(describing: error))")
}
})
}
当将 print 语句放在 for 循环的第一个声明下时(即: print(coverID) ),返回结果就是我所期望的,它循环遍历每个整数,然后按顺序返回它们。
但是,一旦我将相同的打印语句放在“performGetRequestForSpecificCovers”方法下,coverID 数组的顺序就不正确,因此当我附加返回值时,我得到的返回值的顺序不正确到我的“savedTacticalCoverData”数组。
最佳答案
您关于网络请求对排序有影响的预感似乎是正确的。
我猜测这里发生的是,当您循环遍历 tropicalCoverIdArray
并调用 performGetRequestForSpecificCovers()
时,该循环不会等待该网络请求完成并调用完成 block 。它会继续下一次迭代。实际上,您正在并行发送 tropicalCoverIdArray.count
网络请求。这些完成 block 会在很晚的时候被调用,在外部循环完成很久之后,甚至很可能在不同的线程上。
最基本也是最糟糕的选择是使用 DispatchSemaphore 来阻止外部循环,直到调用完成 block 为止。您将创建一个信号量,在完成处理程序内调用 semaphore.signal() ,并在每次循环迭代结束时调用 semaphore.wait() 。这种方法的问题在于,您将等待每个网络请求完成,然后再继续下一个请求。另外,您将占用正在执行第一个外循环的线程,而线程是有限的资源,因此浪费它们并不是一个好主意。
更好的选择是立即分派(dispatch)所有请求,并处理无序响应。这将比串行分派(dispatch)它们完成得快得多,除非您在并行分派(dispatch)如此多的网络请求时遇到某种限制。 savedTacticalCoverData 不是一个数组,也许它可以是一个字典,其中键是外循环的索引,值是您要保存的内容?每次调用完成处理程序时,您都可以检查字典是否已满以及您是否已累积所需的所有响应,然后才继续执行最终的“一切都已完成”操作,大概是 saveTacticalCoverData( )
。
您必须小心才能正确使用多线程。除非 performGetRequestForSpecificCovers()
仅使用一个回调队列,并且该函数运行在同一个队列上,否则您可能会在不同的线程上被调用。如果是这种情况,我建议创建一个新的 DispatchQueue 并始终仅从该队列对字典进行操作,以确保这些完成 block 进入随机线程时的一致性。像这样的事情:
class MyClass {
var tacticalCoverIdArray = [Int]()
var savedTacticalCoverData = [Int: Covers]()
var queue = DispatchQueue(label: "Class Internal Queue")
func myFunc() {
// ... fill in the blanks here
for (index, coverID) in tacticalCoverIdArray.enumerated() {
performGetRequestForSpecificCovers(coverID: coverID, targetURL: targetURL, completion: { (data, HTTPSatusCode, error) -> Void in
if HTTPSatusCode == 200 && error == nil {
do {
queue.async {
if coverID != 0 {
let decodedJSON = try JSONDecoder().decode([Covers].self, from: data)
self.savedTacticalCoverData[index] = decodedJSON[0]
} else {
let data = Covers(id: 0, game: 0, image_id: "")
self.savedTacticalCoverData[index] = data
}
if self.savedTacticalCoverData.count == self.tacticalCoverIdArray.count {
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000), execute: {
self.saveTacticalCoverData()
})
}
}
} catch let error {
print("Error decoding JSON: \(error)")
}
} else {
print("HTTP status code error: \(HTTPSatusCode)")
print("Error loading data: \(String(describing: error))")
}
})
}
}
}
关于arrays - For 循环未按正确的顺序返回数组中的项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56300437/