【问题标题】:For-loop isn't returning the items in an array in the correct orderFor-loop 没有以正确的顺序返回数组中的项目
【发布时间】:2019-10-11 12:23:57
【问题描述】:

我有一个整数数组,我需要循环遍历它,然后发出网络请求以获取更多信息,并使用返回的信息填充我创建的新对象数组。

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

我是开发新手,所以答案可能很明显,但我真的陷入了下一步该怎么做的死胡同。

我尝试在每个循环上为网络请求添加延迟,我尝试在数组上调用 .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 语句(即: print(coverID) 时,返回是我所期望的,它循环遍历每个整数,然后按顺序返回它们。

但是,一旦我将相同的打印语句放在“performGetRequestForSpecificCovers”方法下,coverID 数组的顺序就不是它应该的顺序,因此当我附加它们时,我得到的返回值顺序不正确到我的“savedTacticalCoverData”数组。

【问题讨论】:

    标签: arrays swift sorting for-loop


    【解决方案1】:

    您对网络请求会影响排序的预感似乎是正确的。

    我猜这里发生的事情是,当您循环 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))")
                    }
                })
            }
        }
    }
    

    【讨论】:

    • 太棒了,感谢您的详细回复!如果我能让它为我工作,我会试一试并标记为已回答。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-23
    • 1970-01-01
    • 2017-12-15
    • 1970-01-01
    • 2019-01-08
    • 1970-01-01
    相关资源
    最近更新 更多