Как исправить ошибку «Index Out of Range» в приложении JSON Parsed - PullRequest
0 голосов
/ 03 ноября 2019

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

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

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)

        let searchResult = searchResultsController.searchResults[indexPath.row]
        cell.textLabel?.text = searchResult.title
        cell.detailTextLabel?.text = searchResult.creator
        return cell
    }

    //MARK: Actions

    @IBAction func filterButtonPressed(_ sender: UIBarButtonItem) {
        if limitSearchButton.title == "show 10" {
            limit.limit = "10"
            limitSearchButton.title = "show 5"
            print("limit is \(String(describing: limit.limit))")
            searchBarSearchButtonClicked(searchBar)
            tableView.reloadData()
        } else if limitSearchButton.title == "show 5"{
            limit.limit = "5"
            limitSearchButton.title = "show 10"
            print("limit is \(String(describing: limit.limit))")
            searchBarSearchButtonClicked(searchBar)
            tableView.reloadData()
        }

    }

    @IBAction func segmentedControlChanged(_ sender: UISegmentedControl) {
        if sender.selectedSegmentIndex == 0 {
            searchBarSearchButtonClicked(searchBar)
            self.tableView.reloadData()
        }else if sender.selectedSegmentIndex == 1 {
            searchBarSearchButtonClicked(searchBar)
            self.tableView.reloadData()
        }else {
            searchBarSearchButtonClicked(searchBar)
            self.tableView.reloadData()
        }
    }


}

extension SearchResultsTableViewController: UISearchBarDelegate {
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        guard let searchTerm = searchBar.text else { return }

        switch segmentedControl.selectedSegmentIndex {
        case 0:
            resultType = .software
        case 1:
            resultType = .musicTrack
        case 2:
            resultType = .movie
        default:
            break
        }
        searchResultsController.performSearch(searchTerm: searchTerm, resultType: resultType, limit: limit) {
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    }
}

Я ожидаю, что мои операторы печати и функция фильтра получат предельную сумму и вернут ее вместе с обновлением моей кнопки. Но я ничего не получаю после нескольких использований этой функции UIBarButton.

1 Ответ

0 голосов
/ 07 ноября 2019

Почему происходит сбой?

Это условие гонки между потоками (когда вы получаете новые результаты асинхронно).

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