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

Swift远程搜索TableView时数组为空致崩溃问题求助

解决UITableView搜索时的崩溃问题

这是个iOS开发中很常见的异步数据与UI不同步导致的崩溃问题,我给你梳理几个实用的解决思路,从快速修复到体验优化都有:

1. 立即添加边界检查(快速阻断崩溃)

最直接的防护手段,在cellForRowAt方法里先确认索引是否在数据源数组的有效范围内,避免越界访问:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "TeamSearchResultsCell", for: indexPath) as! TeamSearchResultsCell
    
    // 先做边界校验,确保索引合法
    guard indexPath.row < self.teams.count else {
        // 返回一个空内容的cell,避免崩溃
        cell.TeamNameLable.text = ""
        return cell
    }
    
    cell.TeamNameLable.text = self.teams[indexPath.row].teamName
    return cell
}

这个修改能立刻阻止崩溃,即使UI和数据源暂时不同步,也会优雅处理而不是闪退。

2. 确保TableView行数与数据源严格绑定

检查你的numberOfRowsInSection方法,必须直接返回teams数组的元素数量,绝对不能写固定值或者其他无关逻辑:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.teams.count
}

这是保证TableView展示行数和数据源匹配的基础,从根源减少索引越界的可能。

3. 优化异步回调的数据源更新逻辑

现在的代码是先清空数组、再填充数据、最后刷新UI,这个过程中会有“数组为空但TableView还没刷新”的窗口。可以改成先构建新数组,再原子性替换旧数组并刷新,减少中间异常状态:

loadingJSON("APIPATH", postString:"STRING") { parseJSON in
    if String(describing: parseJSON) == "-1" {
        print("No Internet")
        // 无网络时也要同步更新数据源并刷新UI
        DispatchQueue.main.async {
            self.teams.removeAll()
            self.tableView.reloadData()
        }
    } else {
        if let teamsDictArray = parseJSON["teams"] as? [[String: AnyObject]] {
            // 先在回调里构建完整的新数据数组
            var newTeams = [Team]() // 假设你的数据模型是Team
            for teamDict in teamsDictArray {
                // 解析字典到模型
                if let teamName = teamDict["teamName"] as? String {
                    newTeams.append(Team(teamName: teamName))
                }
            }
            // 回到主线程,一次性替换数据源并刷新UI
            DispatchQueue.main.async {
                self.teams = newTeams
                self.tableView.reloadData()
            }
        }
    }
}

这样就避免了“数组清空后还没填充数据”的尴尬状态,TableView只会看到空数组或者完整的新数组。

4. 增强节流逻辑:取消旧的网络请求

现在的节流只取消了延迟执行的searchTeams调用,但如果之前的网络请求已经发出去,它的回调还是会回来覆盖新结果。可以给每个搜索请求加个任务标识,取消旧请求:

// 定义一个变量保存当前的搜索请求任务
var currentSearchTask: URLSessionTask?

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    // 取消之前延迟执行的搜索任务
    NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(searchTeams), object: nil)
    // 取消正在进行的网络请求
    currentSearchTask?.cancel()
    // 延迟0.3秒执行新搜索
    perform(#selector(searchTeams), with: nil, afterDelay: 0.3)
}

@objc func searchTeams() {
    // 发起新请求前再次取消旧请求(双重保险)
    currentSearchTask?.cancel()
    // 保存新的请求任务
    currentSearchTask = loadingJSON("APIPATH", postString:"STRING") { parseJSON in
        // ... 原有的回调处理逻辑
    }
}

然后修改loadingJSON函数,让它返回创建的任务:

func loadingJSON(_ link:String, postString:String, completionHandler: @escaping (_ JSONObject: AnyObject) -> ()) -> URLSessionTask {
    guard Reachability.isConnectedToNetwork() else {
        completionHandler("-1" as AnyObject)
        // 返回一个已取消的空任务,避免nil问题
        let emptyTask = URLSession.shared.dataTask(with: URL(string: "")!)
        emptyTask.cancel()
        return emptyTask
    }
    
    let request = NSMutableURLRequest(url: URL(string: FinalVars.getMobileRequestURL() + link)!)
    request.httpMethod = "POST"
    request.timeoutInterval = FinalVars.getRequestTimeoutIntervalInSeconds()
    request.httpBody = postString.data(using: .utf8)
    
    let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
        // ... 原有的网络回调逻辑
    }
    task.resume()
    return task
}

这个修改能确保只有最新的搜索请求结果会更新UI,避免旧数据干扰,进一步减少数据源异常的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:57:50