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

滑动删除UITableViewCell时Firebase数据重复更新致崩溃求助

解决UITableView左滑删除后Firebase数据重复更新导致的崩溃问题

我帮你拆解下问题,你遇到的崩溃主要来自两个核心问题:删除逻辑的顺序错误和重复添加Firebase观察者导致的数据混乱,下面一步步给你修复:

一、先搞定左滑删除的逻辑顺序

你现在的代码是先删本地数组里的元素,再去删Firebase的数据,最后刷新表格。这个顺序很容易出问题:

  • 如果Firebase的删除是异步完成的,这期间如果有监听Firebase数据的代码(比如同步本地数组的观察者)触发,就会和你已经修改的本地数组冲突
  • 万一Firebase删除失败,本地数组已经被改了,数据就不一致了

正确的姿势是:先请求Firebase删数据,等它成功返回后,再更新本地数组并刷新表格,这样能保证本地和云端数据完全同步:

修正后的删除代码:

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { 
    let taskToDelete = groupTask[indexPath.row] 
    if editingStyle == .delete { 
        // 先操作Firebase删除数据
        DataService.instance.REF_GROUPS.child(group!.key).child("task").child(taskToDelete.id).removeValue(completionBlock: { (error, refer) in 
            if let error = error {
                print("删除数据失败: \(error.localizedDescription)")
                // 这里可以加个Toast提示用户删除失败
            } else {
                // Firebase删完了,再改本地数组,然后用deleteRows做删除动画,比reloadRows更合理
                self.groupTask.remove(at: indexPath.row)
                self.tableView.deleteRows(at: [indexPath], with: .automatic)
            } 
        }) 
    } 
}

二、修复didSelectRowAt里的坑

你的点击cell代码有两个致命问题:

  1. 每次点击都调用REF_GROUPS.observe(.value),这会不断添加新的持续观察者,如果点个十次,Firebase数据一变就会触发十次回调,重复更新数据、刷新表格,不崩溃才怪
  2. cell.isSelected == true这个判断完全没用(didSelectRowAt触发时cell本来就是选中状态),而且硬写selected: true根本没法实现状态切换

修复方案:

  • 用observeSingleEvent(of: .value)代替observe(.value),因为你只是要更新状态,不需要一直监听数据变化
  • 改成切换任务的选中状态(从本地数组拿当前状态,取反后更新Firebase)
  • 去掉多余的全局reloadData,只刷新当前行,既高效又避免混乱

修正后的点击和更新代码:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
    let selectedTask = groupTask[indexPath.row]
    // 切换选中状态:当前是选中就改成未选中,反之亦然
    let newStatus = !selectedTask.selected
    
    DataService.instance.updateTaskStatus(
        desiredGroup: self.group!, 
        selected: newStatus, 
        childPath: selectedTask.id
    ) { [weak self] in
        // 更新本地数组的状态
        self?.groupTask[indexPath.row].selected = newStatus
        // 只刷新当前点击的行,比全局reloadData性能好太多
        self?.tableView.reloadRows(at: [indexPath], with: .automatic)
    }
    
    // 取消cell选中状态,用户体验更好
    tableView.deselectRow(at: indexPath, animated: true)
}

// 顺便修正updateTaskStatus,把回调触发逻辑补对
func updateTaskStatus(desiredGroup: Group, selected: Bool, childPath: String, handler: @escaping () -> ()) { 
    REF_GROUPS.child(desiredGroup.key).child("task").child(childPath).updateChildValues(["selected": selected]) { error, _ in
        if error == nil {
            // 更新成功再通知回调
            handler()
        } else {
            print("更新状态失败: \(error!.localizedDescription)")
        }
    }
}

最后再提几个注意点

  • 如果你在其他地方(比如页面初始化时)用了observe(.value)监听Firebase数据,一定要在页面销毁时调用removeAllObservers()把观察者清掉,不然会内存泄漏,还会继续触发回调搞乱数据
  • 操作Firebase的时候一定要处理错误,不然出问题了连调试信息都没有
  • 能不用tableView.reloadData()就别用,优先用针对特定行的刷新方法,既快又能避免不必要的UI问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:39:27