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

删除已更新状态的UITableViewCell时应用崩溃问题求助

解决删除已标记任务时的崩溃问题

看起来你的崩溃是两个核心问题叠加导致的:Firebase数据残留引发的类型转换错误,以及数据监听和删除操作的并发冲突。咱们一步步拆解解决:

1. 定位崩溃根源

报错Could not cast value of type 'NSNull' to 'NSString'说明,在解析Firebase返回的任务数据时,你尝试把NSNull(字段不存在时Firebase返回的默认类型)强制转换成NSString。为什么会出现这种情况?因为删除任务后,Firebase里该任务节点居然只剩下selected字段,其他核心字段(比如taskToDo、peopleToDoTask)都消失了——这是因为你的监听逻辑有问题,删除后还在触发更新操作把selected重新写回去。

2. 修复数据监听的并发问题

在didSelectRowAt方法里,你用了REF_GROUPS.observe(.value),这是持续监听,每次点击cell都会新增一个监听实例。当你删除任务时,之前的监听可能还在触发,导致updateTaskStatus又把selected字段写入已删除的节点,最终这个节点只有selected,其他字段都是NSNull。

把持续监听改成单次监听:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    guard let cell = tableView.cellForRow(at: indexPath) as? GroupTaskCell else { return }
    let selectedTask = groupTask[indexPath.row]
    // 建议用模型里的状态反转,而不是依赖cell的isSelected(点击后它本来就是true)
    let newStatus = !selectedTask.selected
    
    // 替换observe(.value)为observeSingleEvent,只获取一次数据
    DataService.instance.REF_GROUPS.observeSingleEvent(of: .value) { _ in
        DataService.instance.updateTaskStatus(
            desiredGroup: self.group!,
            selected: newStatus,
            childPath: selectedTask.id,
            handler: { _ in
                self.tableView.reloadData()
            }
        )
    }
}

3. 处理NSNull的类型转换安全问题

在你的Task模型解析代码里(你没贴出来,但肯定存在这一步),不要用强制转换as!,改用可选绑定+默认值,避免NSNull触发崩溃:

// 假设这是你的Task初始化方法
init(snapshot: DataSnapshot) {
    self.id = snapshot.key
    // 用as? + 默认值处理NSNull情况
    self.taskToDo = snapshot.childSnapshot(forPath: "taskToDo").value as? String ?? ""
    self.peopleToDoTask = snapshot.childSnapshot(forPath: "peopleToDoTask").value as? String ?? ""
    self.senderId = snapshot.childSnapshot(forPath: "senderId").value as? String ?? ""
    self.selected = snapshot.childSnapshot(forPath: "selected").value as? Bool ?? false
}

4. 优化删除逻辑,保证本地与远程数据同步

你现在是先删本地数组再删Firebase数据,这可能导致数据不一致。应该在Firebase删除成功后,再更新本地数组并刷新表格:

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    let taskToDelete = groupTask[indexPath.row]
    if editingStyle == .delete {
        DataService.instance.REF_GROUPS.child(group!.key).child("task").child(taskToDelete.id).removeValue { error, _ in
            if let error = error {
                print("删除失败:\(error.localizedDescription)")
                return
            }
            // 远程删除成功后,再更新本地数据
            self.groupTask.remove(at: indexPath.row)
            self.tableView.deleteRows(at: [indexPath], with: .fade)
            // 用deleteRows比reloadData更高效,也避免索引混乱
        }
    }
}

5. 简化冗余的方法重载

你的updateTaskStatus有两个重载,但其中一个的atIndexpath参数根本没用到,直接合并简化:

func updateTaskStatus(desiredGroup: Group, selected: Bool, childPath: String, handler: @escaping (_ success: Bool) -> ()) {
    REF_GROUPS.child(desiredGroup.key).child("task").child(childPath).updateChildValues(["selected": selected]) { error, _ in
        handler(error == nil)
    }
}

这样修改后,删除已标记的任务就不会再崩溃了,数据同步也会更可靠。

内容的提问来源于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 06:48:51