Ваша догадка о том, что сетевые запросы влияют на порядок, кажется верной.
Я предполагаю, что здесь происходит то, что когда вы перебираете цикл 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))")
}
})
}
}
}