For循环未按正确顺序返回数组中的项目

时间:2019-05-24 23:18:03

标签: arrays swift sorting for-loop

我有一个整数数组,需要遍历整个整数,然后向网络发出进一步的信息请求,并使用返回的信息填充我创建的新对象的数组。

我希望返回的数据以数组提供数据的顺序返回,但是返回的顺序不同,并假设它可能与网络请求有关。

我是新手,所以答案可能很明显,但是下一步的工作我真的走到了尽头。

我尝试在每个循环中向网络请求添加延迟,我尝试在数组上调用.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”数组。

1 个答案:

答案 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))")
                }
            })
        }
    }
}