arrays - 如何修复 JSON 解析应用程序中的“索引超出范围”错误
问题描述
我发现我的代码有一个小问题。我遇到的问题是我的限制调用工作正常,但是如果你运行它并切换显示的数量选项几次,就会出现索引超出范围错误。
只有当你快速运行它或不改变你的搜索时,它似乎才会发生。我在这上面花了 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 函数之后,我什么也没得到。
解决方案
为什么崩溃?
这是线程之间的竞争条件(当您异步获得新结果时)。
有时您尝试显示 4 个元素,但您的数据源刚刚更新,现在有 3 个元素。您在模型更改方面缺乏同步。
由于您对 reloadData 的所有调用,它与网络请求返回的时间相同的可能性更高,我怀疑这会导致崩溃。
API 修复
强制执行并发的一种方法是将所有并发同步工作转移到 API,因此 API 的使用者不必考虑它是如何工作的。(不再有客户端调度异步调用)
在您的 API 逻辑中,使用您的块包装每个completion()
处理程序调用以及对 ViewController 中访问的公共属性的更改。DispatchQueue.main.async { }
该调度队列将强制在不同的线程上不发生 UI 更新。
您在 ViewController 中调用的任何 API 都将位于同一主线程上,因此只要将这些块放在每个适当的位置,您就会获得线程安全。
由于您没有提供该代码,我将提供来自Lambda School的另一个 iOS 项目的代码示例。
你的逻辑在你的 URLSession/networking 代码中看起来像这样:
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()
推荐阅读
- autodesk-forge - 透明物体和隐藏物体可以在 Forge 中共存吗?
- python-3.x - 授权电报时如何发送确认电话
- node.js - Nodejs - 4 个进程和 VS Code 5 个进程占用内存,如任务管理器中所示
- python - 使用 jupyter 笔记本在 M1 Mac 上未定义名称“_mysql”
- symfony - 向 symfony 时间类型小时和分钟字段添加属性
- azure-machine-learning-studio - “MSIAuthentication”对象没有属性“get_token”
- django - Django + Nginx + Gunicorn 设置错误:(104:对等方重置连接)同时从上游读取响应标头
- python - Python烧瓶形式创建组消息,错误绑定参数2 - 可能不支持的类型
- javascript - 按类样式化反应渲染的 HTML
- python - discord.py bot ClientConnectorCertificateError 运行时