此处接 Part 10:iOS的数据持久化(1),文件,归档
Sqlite方式:NoteModelSQlite.swift
import Foundation //Sqlite数据库,需要添加 libsqlite3.tbd 的库,然后创建 .h 头文件,并在头文件中引入 #import "sqlite3.h" //.h 头文件,简单作法:可以先创建一个OC的文件,会提示是否创建头文件,点击确定创建后,头文件会自动创建 class NoteModelSQlite: NSObject { //定义数据库文件名称的常量 let DB_FILE = "/NotesList.sqlite3" //sqlitedb指针 Swift COpaquePointer var db:OpaquePointer? = nil let dateFormatter : DateFormatter = DateFormatter() private static let sharedInstance = NoteModelSQlite() //单例的实例保存这个属性中 class var sharedFoo: NoteModelSQlite { //swift中的静态计算属性 //初始化 sharedInstance.plistCopyDocument() return sharedInstance } //修改Note方法 public func modify(model: Note) { //得到数据库的文件路径 let path:NSString = self.getDocumentPath() as NSString //将path:NSString转换为C的字符串(char*) let cpath = path.cString(using: String.Encoding.utf8.rawValue) /1、sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开 @param1 文件路径, @param2 db指针类型 &db是传递db地址*/ if sqlite3_open(cpath, &db) != SQLITE_OK { sqlite3_close(db)//关闭链接 assert(false, "数据库连接失败") } else { let sql = "update note set content = ? where cdate = ?"//sql语句 //sql语句也需要转换成C的字符串 let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) var statement : OpaquePointer? = nil//statement指针对象 /2、使用sqlite3_prepare_v2函数预处理SQL语句; @param1 db指针 @param2 要执行的sql语句 @param3 要传递sql语句的大小,传递所有 -1 @param4 statement指针对象 @param5 会告诉你那此没有被传递sql语句 / if sqlite3_prepare_v2(db, cSql, -1, &statement, nil) == SQLITE_OK { //3、使用sqlite3_bind_text函数绑定参数;sql语句中的条件会用 ? 号来代替 self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" let strDate = self.dateFormatter.string(from: model.Date as Date)//把model.Date转为字符串 let cDate = strDate.cString(using: String.Encoding.utf8)//转换为C的字符串(char*) let cContent = model.Content.cString(using: String.Encoding.utf8.rawValue)//转换为C的字符串(char*) / sqlite3_bind_text 绑定参数 @param1 statement指针对象 @param2 绑定参数的索引 1开始 @param3 正真要绑定的参数 @param4 要传递的最大字节数 @param5 回调函数 / sqlite3_bind_text(statement, 1, cContent, -1, nil)//绑定content sqlite3_bind_text(statement, 2, cDate, -1, nil)//绑定cdate //4、使用sqlite3_step函数执行SQL语句,遍历结果集; if sqlite3_step(statement) != SQLITE_DONE { //如果没有执行完成 sqlite3_close(db)//关闭链接 assert(false, "信息修改失败") } } // 6、使用sqlite3_finalize和sqlite3_close函数释放资源。 sqlite3_finalize(statement)//释放sql语句对象 sqlite3_close(db) } } //插入Note方法 public func create(model: Note) { //得到数据库的文件路径 let path:NSString = self.getDocumentPath() as NSString //将path:NSString转换为C的字符串(char*) let cpath = path.cString(using: String.Encoding.utf8.rawValue) /1、sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开 @param1 文件路径, @param2 db指针类型 &db是传递db地址*/ if sqlite3_open(cpath, &db) != SQLITE_OK { sqlite3_close(db)//关闭链接 assert(false, "数据库连接失败") } else { let sql = "insert into note (cdate,content) values (?,?)"//sql语句 //sql语句也需要转换成C的字符串 let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) var statement : OpaquePointer? = nil//statement指针对象 /2、使用sqlite3_prepare_v2函数预处理SQL语句; @param1 db指针 @param2 要执行的sql语句 @param3 要传递sql语句的大小,传递所有 -1 @param4 statement指针对象 @param5 会告诉你那此没有被传递sql语句 / if sqlite3_prepare_v2(db, cSql, -1, &statement, nil) == SQLITE_OK { //3、使用sqlite3_bind_text函数绑定参数;sql语句中的条件会用 ? 号来代替 self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" let strDate = self.dateFormatter.string(from: model.Date as Date)//把model.Date转为字符串 let cDate = strDate.cString(using: String.Encoding.utf8)//转换为C的字符串(char*) let cContent = model.Content.cString(using: String.Encoding.utf8.rawValue)//转换为C的字符串(char*) / sqlite3_bind_text 绑定参数 @param1 statement指针对象 @param2 绑定参数的索引 1开始 @param3 正真要绑定的参数 @param4 要传递的最大字节数 @param5 回调函数 / sqlite3_bind_text(statement, 1, cDate, -1, nil)//绑定cdate sqlite3_bind_text(statement, 2, cContent, -1, nil)//绑定content //4、使用sqlite3_step函数执行SQL语句,遍历结果集; if sqlite3_step(statement) != SQLITE_DONE { //如果没有执行完成 sqlite3_close(db)//关闭链接 assert(false, "信息添加失败") } } // 6、使用sqlite3_finalize和sqlite3_close函数释放资源。 sqlite3_finalize(statement)//释放sql语句对象 sqlite3_close(db) } } //删除Note方法 public func remove(model: Note) { //得到数据库的文件路径 let path:NSString = self.getDocumentPath() as NSString //将path:NSString转换为C的字符串(char*) let cpath = path.cString(using: String.Encoding.utf8.rawValue) /1、sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开 @param1 文件路径, @param2 db指针类型 &db是传递db地址*/ if sqlite3_open(cpath, &db) != SQLITE_OK { sqlite3_close(db)//关闭链接 assert(false, "数据库连接失败") } else { let sql = "delete from note where cdate = ?"//sql语句 //sql语句也需要转换成C的字符串 let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) var statement : OpaquePointer? = nil//statement指针对象 /2、使用sqlite3_prepare_v2函数预处理SQL语句; @param1 db指针 @param2 要执行的sql语句 @param3 要传递sql语句的大小,传递所有 -1 @param4 statement指针对象 @param5 会告诉你那此没有被传递sql语句 / if sqlite3_prepare_v2(db, cSql, -1, &statement, nil) == SQLITE_OK { //3、使用sqlite3_bind_text函数绑定参数;sql语句中的条件会用 ? 号来代替 self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" let strDate = self.dateFormatter.string(from: model.Date as Date)//把model.Date转为字符串 let cDate = strDate.cString(using: String.Encoding.utf8)//转换为C的字符串(char*) / sqlite3_bind_text 绑定参数 @param1 statement指针对象 @param2 绑定参数的索引 1开始 @param3 正真要绑定的参数 @param4 要传递的最大字节数 @param5 回调函数 / sqlite3_bind_text(statement, 1, cDate, -1, nil)//绑定cdate //4、使用sqlite3_step函数执行SQL语句,遍历结果集; if sqlite3_step(statement) != SQLITE_DONE { //如果没有执行完成 sqlite3_close(db)//关闭链接 assert(false, "数据库删除失败") } } // 6、使用sqlite3_finalize和sqlite3_close函数释放资源。 sqlite3_finalize(statement)//释放sql语句对象 sqlite3_close(db) } } //根据主键查询一条数据 func findByid(model:Note) -> Note? { //得到数据库的文件路径 let path:NSString = self.getDocumentPath() as NSString //将path:NSString转换为C的字符串(char*) let cpath = path.cString(using: String.Encoding.utf8.rawValue) /1、sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开 @param1 文件路径, @param2 db指针类型 &db是传递db地址*/ if sqlite3_open(cpath, &db) != SQLITE_OK { sqlite3_close(db)//关闭链接 assert(false, "数据库连接失败") } else { let sql = "select cdate,content from note where cdate = ?"//sql语句 //sql语句也需要转换成C的字符串 let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) var statement : OpaquePointer? = nil//statement指针对象 /2、使用sqlite3_prepare_v2函数预处理SQL语句; @param1 db指针 @param2 要执行的sql语句 @param3 要传递sql语句的大小,传递所有 -1 @param4 statement指针对象 @param5 会告诉你那此没有被传递sql语句 / if sqlite3_prepare_v2(db, cSql, -1, &statement, nil) == SQLITE_OK { //3、使用sqlite3_bind_text函数绑定参数;sql语句中的条件会用 ? 号来代替 let strDate = dateFormatter.string(from: model.Date as Date)//把model.Date转为字符串 let cDate = strDate.cString(using: String.Encoding.utf8)//转换为C的字符串(char*) / sqlite3_bind_text 绑定参数 @param1 statement指针对象 @param2 绑定参数的索引 1开始 @param3 正真要绑定的参数 @param4 要传递的最大字节数 @param5 回调函数 / sqlite3_bind_text(statement, 1, cDate, -1, nil) //4、使用sqlite3_step函数执行SQL语句,遍历结果集; if sqlite3_step(statement) == SQLITE_ROW { //有记录返回 /5、使用sqlite3_column_text等函数提取字段数据; @param1 statement指针对象 @param2 字段索引,比如 cdate==0,content==1 @return 返回提char 需要 转为 String / //char* -> String UnsafePointer相当于 char* let bufDate = sqlite3_column_text(statement, 0)//查询cdate let strDate = String(cString: bufDate!)//转为String self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" //String->NSDate let date : NSDate = self.dateFormatter.date(from: strDate)! as NSDate let bufContent = sqlite3_column_text(statement, 1)//查询content let strContent = String(cString: bufContent!)//转为String // 6、使用sqlite3_finalize和sqlite3_close函数释放资源。 sqlite3_finalize(statement)//释放sql语句对象 sqlite3_close(db) let note = Note(date: date, content: strContent as NSString)//合为Note对象 return note } } } return nil } //查询所有数据方法 func findAll() -> NSMutableArray { let listData = NSMutableArray() //得到数据库的文件路径 let path:NSString = self.getDocumentPath() as NSString //将path:NSString转换为C的字符串(char*) let cpath = path.cString(using: String.Encoding.utf8.rawValue) /1、sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开 @param1 文件路径, @param2 db指针类型 &db是传递db地址*/ if sqlite3_open(cpath, &db) != SQLITE_OK { sqlite3_close(db)//关闭链接 assert(false, "数据库连接失败") } else { let sql = "select cdate,content from note"//sql语句 //sql语句也需要转换成C的字符串 let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) var statement : OpaquePointer? = nil//statement指针对象 /2、使用sqlite3_prepare_v2函数预处理SQL语句; @param1 db指针 @param2 要执行的sql语句 @param3 要传递sql语句的大小,传递所有 -1 @param4 statement指针对象 @param5 会告诉你那此没有被传递sql语句 / if sqlite3_prepare_v2(db, cSql, -1, &statement, nil) == SQLITE_OK { //3、使用sqlite3_bind_text函数绑定参数;sql语句中的条件会用 ? 号来代替 //4、使用sqlite3_step函数执行SQL语句,遍历结果集; while sqlite3_step(statement) == SQLITE_ROW { //有记录返回 /5、使用sqlite3_column_text等函数提取字段数据; @param1 statement指针对象 @param2 字段索引,比如 cdate==0,content==1 @return 返回提char 需要 转为 String / let bufDate = sqlite3_column_text(statement, 0)//查询cdate let strDate = String(cString:bufDate!)//cString转为String self.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" //String->NSDate let date : NSDate = self.dateFormatter.date(from: strDate)! as NSDate let bufContent = sqlite3_column_text(statement, 1)//查询content let strContent = String(cString: bufContent!)//转为String let note = Note(date: date, content: strContent as NSString)//合为Note对象 listData.add(note)//添加到可变数组中 } } // 6、使用sqlite3_finalize和sqlite3_close函数释放资源。 sqlite3_finalize(statement)//释放sql语句对象 sqlite3_close(db) } return listData } //初始化 创建数据表 与 数据库文件 func plistCopyDocument() { //得到数据库的文件路径 let path:NSString = self.getDocumentPath() as NSString print(path) //将path:NSString转换为C的字符串(char*) let cpath = path.cString(using: String.Encoding.utf8.rawValue) /sqlite3_open,打开数据库,如果数据库文件不存在则创建,存在则打开 @param1 文件路径, @param2 db指针类型 &db是传递db地址*/ if sqlite3_open(cpath, &db) != SQLITE_OK { sqlite3_close(db)//关闭链接 assert(false, "数据库连接失败") } else { //sql语句:如果Note数据表不存在则创建,cdate 是主键,content为字符类型 let sql = "create table if not exists Note (cdate text primary key, content text)" //sql语句也需要转换成C的字符串 let cSql = sql.cString(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) /sqlite3_exec执行sql,但sqlite3_exec没有返回结果集 @param1 指针, @param2 sql语句, @param3 回调函数, @param4 为回调函数传递参数的指针 @param5 errmsg错误信息*/ if sqlite3_exec(db, cSql, nil, nil, nil) != SQLITE_OK { sqlite3_close(db)//关闭链接 assert(false, "SQL语句执行失败") } else { sqlite3_close(db)//执行成功关闭链接 } } } //获取系统沙箱Document目录方法 func getDocumentPath() -> String { let documentDirectory: NSArray = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray let myDocPath = documentDirectory[0] as! NSString //在myDocPath字符串的基础上累加另一个字符串NotesList.plist,目录+文件路径 let wtFile = myDocPath.appending(DB_FILE) as String return wtFile } }
CoreData方式:NoteDAO.swift
//如果使用CoreData,下以步骤 //1,创建CoreData文件,在对应文件夹中右击选择CoreData.. //2,创建CoreData模型,进入CoreData文件中,左下方的加号 //3,生成实例文件,注意命名不要和现有类冲突,选中Model.xcdatamodeld->Editor->Create NSManagedObject subclass //4,拷贝堆栈代码(创建新项目 use CoreData ,AppDelegate.swift中查看)或创建到指定文件位置,注意命名.. //5,要引入 import CoreData //注:这个是一种convenience方法,即快速实现。所以并不需要新建对应于entity的class,上面第4步可省略 import UIKit import Foundation import CoreData //DAO类,一般按表名命名,相当于PHP框架加的 Model class NoteDAO: UIViewController { //保存数据列var,没用数据,数据保存到内存中 var listData: NSMutableArray! //可变数组类型 //只进行和数据库的交互,不需要保持任何状态,所以可以使用单例模式 private static let sharedInstance = NoteDAO() //单例的实例保存这个属性中 class var sharedFoo: NoteDAO { //swift中的静态计算属性 return sharedInstance } //插入Note方法 func create(model: Note) { let context = self.getContext() // 定义一个entity,这个entity一定要在xcdatamodeld中做好定义 let entity = NSEntityDescription.entity(forEntityName: "CoreDataNote", in: context) let person = NSManagedObject(entity: entity!, insertInto: context) person.setValue(model.Content, forKey: "cContent") person.setValue(model.Date, forKey: "cDate") do { try context.save() print("saved") }catch{ print(error) } } //删除Note方法 func remove(model: Note) { //定义查询主体 let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "CoreDataNote") let entity:NSEntityDescription = NSEntityDescription.entity(forEntityName: "CoreDataNote", in: getContext())! //指定要查询的实体 fetchRequest.entity = entity //添加查询条件 fetchRequest.predicate = NSPredicate(format: "cDate = %@", model.Date) do { //进行查询 let searchResults = try getContext().fetch(fetchRequest) if searchResults.count != 0 { //删除操作 self.getContext().delete(searchResults[0] as! NSManagedObject) //结果保存 try getContext().save() print("delete success ~ ~") } } catch { print(error) } } //修改Note.Content方法 func modify(model: Note) { //定义查询主体 let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "CoreDataNote") let entity:NSEntityDescription = NSEntityDescription.entity(forEntityName: "CoreDataNote", in: getContext())! //指定要查询的实体 fetchRequest.entity = entity //添加查询条件 fetchRequest.predicate = NSPredicate(format: "cDate = %@", model.Date) do { //进行查询 let searchResults = try getContext().fetch(fetchRequest) if searchResults.count != 0 { //到得查询出来的对象 let cNote = searchResults[0] as! CoreDataNote //修改对应的字段 cNote.cContent = model.Content as String //结果保存 try getContext().save() print("update success ~ ~") } } catch { print(error) } } //查询所有数据方法 func findAll() -> NSMutableArray { //定义可变数组 let listData = NSMutableArray() //定义查询主体 let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "CoreDataNote") do { //进行查询 let searchResults = try getContext().fetch(fetchRequest) //print("numbers of \(searchResults.count)")//获得总数 //遍历结果集 for p in (searchResults as! [NSManagedObject]){ //获取对应字段 let date = p.value(forKey: "cDate") as! NSDate let content = p.value(forKey: "cContent") as! NSString //创建一个 note 对像 let note = Note(date: date, content: content) //加入可变数组中 listData.add(note) } } catch { print(error) } return listData } //查询一条数据方法 func findById(model: Note) -> String! {//返回类型为可选的,说明Note可以返回nil //定义查询主体 let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "CoreDataNote") let entity:NSEntityDescription = NSEntityDescription.entity(forEntityName: "CoreDataNote", in: getContext())! //指定要查询的实体 fetchRequest.entity = entity //添加查询条件 fetchRequest.predicate = NSPredicate(format: "cDate = %@", model.Date) do { //进行查询 let searchResults = try getContext().fetch(fetchRequest) if searchResults.count != 0 { //到得查询出来的对象 let cNote = searchResults[0] as! CoreDataNote //查询需要的字段值 let cContent = cNote.value(forKey: "cContent")! //返回 return cContent as! String } } catch { print(error) } return nil } // 获取Context上下文 func getContext () -> NSManagedObjectContext { let appDelegate = UIApplication.shared.delegate as! AppDelegate return appDelegate.persistentContainer.viewContext } }