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

Swift+SQLite3开发学生数据存储APP的查询显示异常求助

解决Swift SQLite学生数据显示异常问题

我看到你遇到的问题了——这是因为在绑定SQLite参数的时候,你传递了一个可选类型的字符串,导致SQLite没有正确读取姓名的值,反而意外获取到了日期字段的部分内容。让我一步步帮你解决这个问题:

问题根源分析

在buttonSave方法中,你获取的name是Optional<String>类型,直接传递给sqlite3_bind_text时,Swift的自动桥接无法正确处理可选值,导致SQLite没有正确绑定姓名参数。这就使得数据库中name字段被意外填充了date字段的部分内容,最终在列表中显示出日期的切片。

另外,你的代码还有两个额外的小问题:

  • viewDidLoad中每次启动都会执行DROP TABLE Students,导致之前存储的所有数据丢失
  • cellForRowAt方法没有复用UITableViewCell,会影响滚动性能

具体修复步骤

1. 修复SQLite参数绑定的可选解包

在绑定姓名参数时,因为你已经通过非空判断确认了姓名有效,所以可以安全地解包name,同时建议将最后一个参数改为SQLITE_TRANSIENT,让SQLite自动复制字符串内容,避免Swift字符串被释放后出现内存问题:

// 原姓名绑定代码
if sqlite3_bind_text(stmt, 1, name, -1, nil) != SQLITE_OK{
// 修改为
if sqlite3_bind_text(stmt, 1, name!, -1, SQLITE_TRANSIENT) != SQLITE_OK{

日期绑定也做同样修改:

// 原日期绑定代码
if sqlite3_bind_text(stmt, 2, date, -1, nil) != SQLITE_OK{
// 修改为
if sqlite3_bind_text(stmt, 2, date, -1, SQLITE_TRANSIENT) != SQLITE_OK{

2. 移除不必要的表删除语句

删除viewDidLoad中的DROP TABLE Students语句,这样重启APP后之前存储的数据不会丢失:

// 移除以下代码块
// if sqlite3_exec(db, "DROP TABLE Students", nil, nil, nil) != SQLITE_OK {
//     let errmsg = String(cString: sqlite3_errmsg(db)!)
//     print("error deleting table: \(errmsg)")
// }

3. 优化UITableViewCell复用

在cellForRowAt方法中使用单元格复用,提升滚动性能:

// 原代码
let cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
// 修改为
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) ?? UITableViewCell(style: .default, reuseIdentifier: "cell")

同时记得在Storyboard中给你的TableViewCell设置reuseIdentifier为"cell",确保复用正常工作。

4. 可选:优化日期存储(推荐)

目前你将日期存储为字符串,推荐存储时间戳(Int64),这样后续处理日期会更灵活:

// 存储时
let timestamp = Int64(Date().timeIntervalSince1970)
// 绑定参数时用sqlite3_bind_int64
if sqlite3_bind_int64(stmt, 2, timestamp) != SQLITE_OK{
    let errmsg = String(cString: sqlite3_errmsg(db)!)
    print("failure binding date: \(errmsg)")
    return
}

// 读取时
let timestamp = sqlite3_column_int64(stmt, 2)
let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
// 再转换为需要的字符串格式
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy"
let dateString = formatter.string(from: date)

修改后的完整代码

import UIKit
import SQLite3

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    var db: OpaquePointer?
    var Students = [Student]()
    
    @IBOutlet weak var textFieldName: UITextField!
    
    @IBAction func buttonSave(_ sender: UIButton) {
        // 使用guard let更安全地处理可选值
        guard let name = textFieldName.text?.trimmingCharacters(in: .whitespacesAndNewlines), !name.isEmpty else {
            textFieldName.layer.borderColor = UIColor.red.cgColor
            return
        }
        
        let d = Date()
        let formatter = DateFormatter()
        formatter.dateFormat = "dd.MM.yyyy"
        let date = formatter.string(from: d)
        
        var stmt: OpaquePointer?
        let queryString = "INSERT INTO Students (name, date) VALUES (?,?)"
        
        if sqlite3_prepare(db, queryString, -1, &stmt, nil) != SQLITE_OK{
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("error preparing insert: \(errmsg)")
            return
        }
        
        if sqlite3_bind_text(stmt, 1, name, -1, SQLITE_TRANSIENT) != SQLITE_OK{
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("failure binding name: \(errmsg)")
            return
        }
        
        if sqlite3_bind_text(stmt, 2, date, -1, SQLITE_TRANSIENT) != SQLITE_OK{
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("failure binding date: \(errmsg)")
            return
        }
        
        if sqlite3_step(stmt) != SQLITE_DONE {
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("failure inserting student: \(errmsg)")
            return
        }
        
        textFieldName.text=""
        readValues()
        print("Student saved successfully")
    }
    
    @IBOutlet weak var tableViewStudents: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("StudentsDatabase.sqlite")
        
        if sqlite3_open(fileURL.path, &db) != SQLITE_OK {
            print("error opening database")
        }
        
        if sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS Students (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, date TEXT)", nil, nil, nil) != SQLITE_OK {
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("error creating table: \(errmsg)")
        }
        
        // 初始化时读取已有数据
        readValues()
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return Students.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) ?? UITableViewCell(style: .default, reuseIdentifier: "cell")
        let student = Students[indexPath.row]
        // 同时显示姓名和日期,方便验证结果
        cell.textLabel?.text = "\(student.name) - \(student.date)"
        return cell
    }
    
    func readValues(){
        Students.removeAll()
        
        let queryString = "SELECT * FROM Students"
        var stmt:OpaquePointer?
        
        if sqlite3_prepare(db, queryString, -1, &stmt, nil) != SQLITE_OK{
            let errmsg = String(cString: sqlite3_errmsg(db)!)
            print("error preparing select: \(errmsg)")
            return
        }
        
        while(sqlite3_step(stmt) == SQLITE_ROW){
            let id = sqlite3_column_int(stmt, 0)
            let name = String(cString: sqlite3_column_text(stmt, 1))
            let date = String(cString: sqlite3_column_text(stmt, 2))
            
            Students.append(Student(id: Int(id), name: name, date: date))
        }
        
        tableViewStudents.reloadData()
    }
}

class Student {
    var id: Int
    var name: String
    var date: String
    
    init(id: Int, name: String, date: String){
        self.id = id
        self.name = name
        self.date = date
    }
}

额外提示

  • 建议使用guard let来处理可选值,比强制解包更安全(我已经在修改后的代码中替换了原有的可选判断)
  • 可以考虑封装SQLite操作到单独的工具类中,让代码结构更清晰

内容的提问来源于stack exchange,提问作者Valeria S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:40:16