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

Swift中删除TableView行与SQL数据时最后一行报错求助

问题分析与解决方案

我一眼就发现了问题的核心,咱们一步步拆解解决:

核心错误:索引越界 + 错误的ID传递

先看你这段TableView删除逻辑的代码:

notes.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .bottom)
NoteManager.shared.deleteNote(noteId: notes[indexPath.row].id)

你犯了一个典型的顺序错误:先删除了数组里的元素,再去拿这个索引对应的ID。这会导致两种问题:

  • 删除非最后一行时:数组长度变短,你拿到的是原索引位置下一个元素的ID——这就是为什么你看到打印的ID从4开始递增,相当于删了A元素,却把B元素的ID传给SQL删除了!
  • 删除最后一行时:数组直接为空,indexPath.row已经超出数组边界,直接触发数组越界崩溃。

第一步:修复顺序问题

调整代码逻辑,先获取要删除的Note的ID,再执行数组和TableView的删除操作:

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        // 1. 先拿到要删除的Note和它的ID
        let targetNote = notes[indexPath.row]
        let targetId = targetNote.id
        
        // 2. 再删除数组元素和TableView行
        notes.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .bottom)
        
        // 3. 最后调用SQL删除
        NoteManager.shared.deleteNote(noteId: targetId)
    }
}

这样既不会出现索引越界,也能正确传递要删除的ID给SQL函数。

第二步:优化SQL删除函数的安全性与错误处理

你的SQL函数还有两个可以完善的地方:

  1. 字符串拼接SQL有注入风险:直接把noteId拼进SQL语句,虽然这里是Int32,但最好养成参数绑定的好习惯;
  2. 错误信息不够具体:只打印通用错误,没法快速定位问题。

优化后的deleteNote函数:

func deleteNote(noteId: Int32) {
    connect()
    guard let database = database else {
        print("Database connection failed")
        return
    }
    
    var statement: OpaquePointer? = nil
    // 用?作为占位符,避免SQL注入
    let deleteQuery = "DELETE FROM notes WHERE rowid = ?"
    
    if sqlite3_prepare_v2(database, deleteQuery, -1, &statement, nil) == SQLITE_OK {
        // 绑定参数,占位符从1开始计数
        sqlite3_bind_int(statement, 1, noteId)
        
        if sqlite3_step(statement) != SQLITE_DONE {
            let errorMsg = String(cString: sqlite3_errmsg(database))
            print("Error deleting note: \(errorMsg)")
        }
    } else {
        let errorMsg = String(cString: sqlite3_errmsg(database))
        print("Error creating delete statement: \(errorMsg)")
    }
    sqlite3_finalize(statement)
}

验证效果

现在再测试删除操作:

  • 删除最后一行不会崩溃,因为已经提前拿到了正确的ID;
  • 打印的ID会和你实际删除的Note完全匹配,不会再出现递增的错误。

内容的提问来源于stack exchange,提问作者Michał Śniady

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 15:28:13