我有一个整数数组,需要遍历整个整数,然后向网络发出进一步的信息请求,并使用返回的信息填充我创建的新对象的数组。
我希望返回的数据以数组提供数据的顺序返回,但是返回的顺序不同,并假设它可能与网络请求有关。
我是新手,所以答案可能很明显,但是下一步的工作我真的走到了尽头。
我尝试在每个循环中向网络请求添加延迟,我尝试在数组上调用.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))")
}
})
}
当将打印语句放在for循环的第一个声明下时(即:print(coverID)),返回值是我所期望的,它在其中循环遍历每个整数,然后按顺序返回它们。
但是,一旦我在“ performGetRequestForSpecificCovers”方法下放置了相同的打印语句,coverID数组就没有应按其应有的顺序排列,因此在添加它们时,返回的值以不正确的顺序排列到我的“ savedTacticalCoverData”数组。
答案 0 :(得分:0)
您对网络请求产生影响的直觉似乎是正确的。
我猜这里正在发生的事情是,当您遍历tacticalCoverIdArray
并调用performGetRequestForSpecificCovers()
时,该循环不会等待该网络请求完成并且不会等待完成块叫。继续下一次迭代。实际上,您正在并行发送tacticalCoverIdArray.count
个网络请求。这些完成块会在外部循环完成之后很久以后被调用,甚至在不同的线程上也很可能被调用。
最基本也是最糟糕的选择是使用DispatchSemaphore阻止外部循环,直到调用完成块。您将创建一个信号量,在完成处理程序内部调用semaphore.signal()
,并在每次循环迭代结束时调用semaphore.wait()
。这种方法的问题在于,在继续下一个请求之前,您将等待每个网络请求完成。另外,您将占用执行第一个外部循环的线程,并且线程是有限的资源,因此浪费它们不是一个好主意。
更好的选择是一次分派所有请求,并处理乱序响应。这将比串行调度它们快得多,除非您在并行调度这么多的网络请求时遇到某种限制。而不是savedTacticalCoverData
是一个数组,也许它可能是一个字典,其中的键是外部循环的索引,而值是您要保存的内容?每次调用完成处理程序时,您可以检查字典是否已满,并且已经积累了所有想要的响应,然后才执行最后的“一切都完成”操作,大概是saveTacticalCoverData()
。
您必须注意正确使用多线程。除非performGetRequestForSpecificCovers()
仅使用一个回调队列,并且此函数在同一队列上运行,否则您可能会在不同的线程上被调用。如果是这种情况,我建议您创建一个新的DispatchQueue并始终仅在该队列中对字典进行操作,以确保在随机线程上插入这些完成块时保持一致性。像这样:
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))")
}
})
}
}
}