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

标签 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))")
        }
    })
}

当将 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/

相关文章:

php - 如何在 PHP 中按字母顺序比较 2 个字符串?

php - 在特定条件下使用 PHP 进行排名

javascript - 如何分割两个或多个地址

arrays - 动态填充空单元格

swift - 如何检查文档目录中是否存在多个文件? ( swift )

ios - swift - 如何完成任务

javascript - 如何根据起始字母过滤数组

php - 如何使用zend框架转换json数据中的对象?

ios - 如何使用参数切换到 ViewController 或 TableViewController?

php 对多维数组进行排序,但从 1 开始索引