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

Swift中SQLite删除TableView行失效问题排查

问题分析与解决方案

你遇到的核心问题是删除时用了表格行号作为数据库主键ID,导致SQL语句匹配不到实际要删除的行,而SQLite的DELETE语句即使没有匹配到任何行,也会返回SQLITE_DONE,所以你看到了"Successfully deleted row."的提示,但数据库里的数据根本没被改动。

为什么会这样?

你的BrainDump表中id是自增主键(INTEGER PRIMARY KEY AUTOINCREMENT),它的值是数据库自动生成的唯一标识,和UITableView的行号完全无关:

  • 比如你添加3条数据,它们的id是1、2、3,对应表格行号0、1、2
  • 当你删除行号0的条目(id=1)后,剩下的两条数据行号变成0、1,但它们的数据库id还是2、3
  • 这时候你再用行号0作为id传去删除,数据库里根本没有id=0的行,自然删不掉

修复步骤

1. 修改删除逻辑,使用真实的数据库ID

在tableView(_:commit:forRowAt:)方法中,不要用行号,而是从你的数据源数组brainList中获取对应条目的真实id:

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    // 获取当前行对应的真实数据库ID
    let targetBrainDump = brainList[indexPath.row]
    deleteItem(itemId: Int32(targetBrainDump.id))
    
    // 同步更新数据源和UI(两种方式选其一)
    // 方式1:直接移除数组元素并刷新对应行(更高效)
    brainList.remove(at: indexPath.row)
    tableView.deleteRows(at: [indexPath], with: .fade)
    
    // 方式2:重新从数据库读取所有数据(适合数据量小的场景)
    // readValues()
}

2. 优化删除函数,验证实际删除效果

可以通过sqlite3_changes(db)获取被修改的行数,这样能准确知道是否真的删除了数据,而不是只看语句是否执行成功:

func deleteItem(itemId: Int32) {
    let deleteStatementString = "DELETE FROM BrainDump WHERE id = ?;"
    var deleteStatement: OpaquePointer? = nil
    
    if sqlite3_prepare_v2(db, deleteStatementString, -1, &deleteStatement, nil) == SQLITE_OK {
        sqlite3_bind_int(deleteStatement, 1, itemId)
        
        if sqlite3_step(deleteStatement) == SQLITE_DONE {
            let affectedRows = sqlite3_changes(db)
            if affectedRows > 0 {
                print("Successfully deleted \(affectedRows) row(s).")
            } else {
                print("Warning: No row found with id \(itemId) to delete.")
            }
        } else {
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("Failed to execute delete: \(errmsg)")
        }
    } else {
        let errmsg = String(cString: sqlite3_errmsg(db)!)
        print("Failed to prepare DELETE statement: \(errmsg)")
    }
    sqlite3_finalize(deleteStatement)
}

额外小建议

  • 你的readValues()方法中,String(describing: name)是多余的,因为sqlite3_column_text返回的C字符串转成String已经是正确的文本了,可以直接写成name: name
  • 在处理文本字段非空判断时,尽量避免强制解包(!),可以用可选绑定更安全:
    guard let name = textFieldName.text?.trimmingCharacters(in: .whitespacesAndNewlines), !name.isEmpty else {
        textFieldName.layer.borderColor = UIColor.red.cgColor
        return
    }
    guard let powerRankingStr = textFieldPowerRanking.text?.trimmingCharacters(in: .whitespacesAndNewlines), 
          let powerRanking = Int32(powerRankingStr), !powerRankingStr.isEmpty else {
        textFieldPowerRanking.layer.borderColor = UIColor.red.cgColor
        return
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:44:39