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
相关产品推荐
相关产品推荐

