You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中SearchController搭配TableView点击cell触发Index out of range错误求解

崩溃根因分析

核心问题点

  • 异步请求和UI刷新时序错位:当前代码调用fetchKeywords发起网络请求后立刻执行reloadData,但网络请求是异步执行的,此时ViewModel的results数组还未更新。等到请求成功回调中修改results后,没有触发UI刷新,UITableView记录的行数和results实际长度不一致,用户点击超出新数组长度的行就会触发越界崩溃。
  • 数组下标无边界校验:searchKeyword、selectedKeyword方法中直接使用下标访问数组,没有判断下标是否在合法范围内,只要下标超出数组长度必然崩溃。
  • 协议方法重复实现:你重复写了两次UISearchResultsUpdating协议的扩展,两个方法逻辑相似但刷新的视图对象不同(一个刷searchTableView,一个刷searchResults),会导致逻辑混乱、重复发起请求,进一步加大了时序错乱的概率。
  • 无搜索防抖和请求去重:输入每个字符都触发请求,会出现后发的请求先返回、旧请求结果覆盖新请求结果的问题,进一步加剧数据和UI的不一致。
修复方案

1. 修正异步刷新逻辑

网络请求成功更新results后,再通知ViewController刷新UI,所有UI操作必须切回主线程执行:

ViewModel修改

// 新增结果更新回调
var onResultsUpdated: (() -> Void)?

func fetchKeywords(searchKeyword: String) {
    self.useCase.fetchKeywords(searchKeyword: searchKeyword, paginationCount: 5) { [weak self] error, result in
        guard let self = self else { return }
        if let error = error {
            self.error = error
        }
        if let result = result {
            self.results = result.searchKeywords
        }
        // 切主线程通知UI刷新
        DispatchQueue.main.async {
            self.onResultsUpdated?()
        }
    }
}

ViewController修改

初始化ViewModel时绑定回调:

viewModel?.onResultsUpdated = { [weak self] in
    self?.searchTableView.reloadData()
}

删除updateSearchResults中立刻刷新的代码:

extension ViewController: UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {
        guard let text = searchController.searchBar.text?.lowercased() else { return }
        self.viewModel?.fetchKeywords(searchKeyword: text)
        // 删掉这里的reloadData,移到ViewModel的回调里执行
    }
}

2. 增加数组下标边界校验

修改ViewModel中两个下标访问的方法,先判断下标合法性再取值:

func searchKeyword(indexPath: Int) -> String? {
    guard let results = results, indexPath >= 0, indexPath < results.count else { return nil }
    return results[indexPath].name
}

func selectedKeyword(at indexPath: Int) -> Keyword? {
    guard let results = self.results, indexPath >= 0, indexPath < results.count else { return nil }
    return results[indexPath]
}

3. 清理重复代码

直接删掉第二个重复实现UISearchResultsUpdating的扩展(也就是包含updateResults方法的那个扩展),避免逻辑冲突。

4. 增加搜索防抖

减少频繁请求,降低时序错乱概率,在ViewController中增加定时器实现防抖:

// 增加防抖定时器属性
private var searchTimer: Timer?

extension ViewController: UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {
        guard let text = searchController.searchBar.text?.lowercased() else { return }
        // 取消之前的待执行请求
        searchTimer?.invalidate()
        // 输入停止300ms后再发起请求
        searchTimer = Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false) { [weak self] _ in
            self?.viewModel?.fetchKeywords(searchKeyword: text)
        }
    }
}

内容的提问来源于stack exchange,提问作者panther222128

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 03:15:08