此处接 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
}
}