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函数还有两个可以完善的地方:
- 字符串拼接SQL有注入风险:直接把
noteId拼进SQL语句,虽然这里是Int32,但最好养成参数绑定的好习惯; - 错误信息不够具体:只打印通用错误,没法快速定位问题。
优化后的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
相关产品推荐
相关产品推荐

