Почему происходит сбой?
Это условие гонки между потоками (когда вы получаете новые результаты асинхронно).
Иногда вы пытаетесь отобразить 4 элемента, но ваш источник данных только что обновился, и теперь есть 3 элемента. Вам не хватает синхронизации при смене модели.
При всех ваших вызовах reloadData есть большая вероятность того, что это будет одновременно с возвратом сетевого запроса, что, как я подозреваю, приводит к сбою.
API Fix
Один из способов обеспечения параллелизма - перенести всю работу по синхронизации параллелизма на API, поэтому потребителю API не нужно думать о том, как он работает. (больше не нужно отправлять асинхронные вызовы на стороне клиента)
В своей логике API оберните каждый вызов обработчика completion()
и внесите изменения в публичные свойства, доступные в ViewController, с вашим блоком DispatchQueue.main.async { }
. Эта очередь отгрузки обеспечит отсутствие обновлений пользовательского интерфейса в другом потоке.
Все API, которые вы вызываете в ViewController, будут находиться в одном и том же главном потоке, так что вы получите безопасность потока, если поместите эти блоки в каждое подходящее место.
Поскольку вы не сделалиЧтобы предоставить этот код, я предоставлю пример кода из другого проекта iOS в Lambda School .
Ваша логика будет выглядеть примерно так в вашем URLSession / network code:
URLSession.shared.dataTask(with: url) { (data, _, error) in
if let error = error {
print("Error fetching quakes: \(error)")
DispatchQueue.main.async { // Wrap every call to completion()
completion(nil, error)
}
return
}
guard let data = data else {
DispatchQueue.main.async {
completion(nil, QuakeError.noDataReturned)
}
return
}
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .millisecondsSince1970
let quakeResults = try decoder.decode(QuakeResults.self, from: data)
DispatchQueue.main.async {
// Setting a public property needs to be also protected
// inside your main.async blocks, in addition to your
// completion() handler calls
self.quakes = quakeResults.features // protect public properties
completion(quakes, nil)
}
} catch {
DispatchQueue.main.async {
completion(nil, error)
}
}
}.resume()