Цикл for не возвращает элементы в массиве в правильном порядке - PullRequest
0 голосов
/ 25 мая 2019

У меня есть массив целых чисел, которые мне нужно перебрать, затем сделать сетевой запрос для получения дополнительной информации и с возвращенной информацией заполнить массив нового объекта, который я создал.

Я хочу, чтобы возвращаемые данные возвращались в том же порядке, в котором их предоставляет массив, однако они возвращаются в другом порядке, и я предполагаю, что это может иметь какое-то отношение к сетевому запросу.

Я новичок в разработке, поэтому ответ может быть очень очевидным, но я действительно зашел в тупик с тем, что делать дальше.

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

Однако, как только я помещаю ту же инструкцию печати в метод «executeGetRequestForSpecificCovers», массив coverID находится не в том порядке, в котором он должен быть, и поэтому я получаю возвращенные значения в неправильном порядке, когда добавляю их в свой ».Массив saveTacticalCoverData '.

1 Ответ

0 голосов
/ 25 мая 2019

Ваша догадка о том, что сетевые запросы влияют на порядок, кажется верной.

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