iOS之数据持久化进阶(plist、SQLite、CoreData)

21 篇文章 0 订阅

简介

  • 持久化方式就是数据存储方式.iOS支持本地存储和云端存储,而本地存储主要涉及如下三种机制:
  • 属性列表:集合对象可以读写到属性列表中;
  • SQLite数据库:SQLite是一个开源嵌入式关系型数据库;
  • CoreData:是一种对象关系映射技术(ORM),本质上也是通过SQLite存储.
  • 属性列表文件一般用于存储少量数据,Foundation框架中的集合对象都有对应的方法读写属性列表文件了;SQLite数据库和CoreData一般用于有几个简单表关系的大量数据情况。如果是复杂表关系而且数据量很大,我们应该考虑把数据放在远程服务器上。
  • 分别用属性列表,SQLite,CoreData来做一个类似备忘录的增删改查,效果如下。
    在这里插入图片描述

属性列表

  • 数据列表本质是XML文件,Foundation框架中的数组和字典等都可以与属性列表文件互相转换。

    1. + (nullable NSArray<ObjectType> *)arrayWithContentsOfFile:(NSString *)path;:类工厂方法,从属性列表读取数据,创建NSArray对象,Swift没有对应的构造函数。
    2. - (nullable NSArray<ObjectType> *)initWithContentsOfFile:(NSString *)path; :构造函数,从属性列表读取数据,创建NSArray对象;Swift语言表示为public convenience init?(contentsOfFile path: String)
    3. - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;:将NSArray或者NSDictionary对象写入到属性列表中,参数path是属性类表文件路径,useAuxiliaryFile表示是否使用辅助文件,如果设置为YES则先写入辅助文件,然后将辅助文件重写命名为目标文件;如果为NO,则直接写入目标文件,返回的布尔值为YES则边上写入成功,否写入失败。对应的Swift方法open func write(toFile path: String, atomically useAuxiliaryFile: Bool) -> Bool
    4. + (nullable NSDictionary<KeyType, ObjectType> *)dictionaryWithContentsOfFile:(NSString *)path;类工厂方法,从属性列表读取数据,创建NSDictionary对象,Swift没有对应的构造函数。
    5. - (nullable NSDictionary<KeyType, ObjectType> *)initWithContentsOfFile:(NSString *)path;:构造函数,从属性列表读取数据,创建NSDictionary对象;Swift语言表示为public convenience init?(contentsOfFile path: String)
  • 关键代码类

数据模型类

import Foundation
//数据模型
public class Note {
    var date: Date
    var content: String
    init(date: Date,content: String) {
        self.date = date
        self.content = content
    }
    
    init() {
        self.date = Date()
        self.content = ""
    }
}

数据操作类

import Foundation
//用来访问属性列表文件的工具类
public class NoteDAO {
   private var dateFormatter = DateFormatter()
   //属性类表访问路劲
   private var plistFileParh:String!
   public static let shareInstance: NoteDAO = {
       let instance = NoteDAO()
       //属性列表路劲
       instance.plistFileParh = instance.applicationDocumentsDirectoryFile
       instance.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
       //初始化属性列表文件
       instance.createEditableCopyOfDatabaseIfNeeded()
       return instance
   }()
   private func createEditableCopyOfDatabaseIfNeeded(){
       let fileManager = FileManager.default
       //判断属性列表是否已经存在
       let dbexits = fileManager.fileExists(atPath: self.plistFileParh)
       if (!dbexits){
           //获取Bundle,通过该对象可以访问当前程序的资源目录,而下面的构造方法可以避免当前文件拉到别的项目用因为项目名称不对而访问不到
           let frameworkBundle = Bundle(for: NoteDAO.self)
//            let defaultDBPath = Bundle.main.path(forResource:"NotesList", ofType: "plist")
           let frameworkBundlePath = frameworkBundle.resourcePath as NSString?
           let defaultDBPath = frameworkBundlePath!.appendingPathComponent("NotesList.plist")
           do{
               //实现文件复制
               try fileManager.copyItem(atPath: defaultDBPath, toPath: self.plistFileParh)
               
           }catch{
               let nserror = error as NSError
               print("数据保存错误:\(nserror.localizedDescription)")
               assert(false, "错误写入文件")
           }
       }
   }
   let applicationDocumentsDirectoryFile: String = {
       let documentDirectory: NSArray = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
       let basePath = documentDirectory.firstObject as!String
       let path = basePath + "/" + "NotesList.plist"
       return path
   }()
   
   //插入Note数据模型到属性列表中
   public func create(_ mode: Note) -> Int{
       let array  = NSMutableArray(contentsOfFile: self.plistFileParh)!
       let strDate = self.dateFormatter.string(from: mode.date as Date)
       let dict = NSDictionary(objects: [strDate, mode.content], forKeys: ["date" as NSCopying,"content" as NSCopying])
       array.add(dict)
       array.write(toFile: self.plistFileParh, atomically: true)
       return array.count - 1
   }
   //从属性列表中删除数据
   public func remove(_ model : Note) -> Int{
       let array = NSMutableArray(contentsOfFile: self.plistFileParh)!
       var index = 0
       for item in array {
           let dict = item as!NSDictionary
           let strDate = dict.value(forKey: "date")as!String
           let date = dateFormatter.date(from: strDate)!
           index += 1
           //比较日期主键是否相等
           if date == model.date as Date{
               array.remove(dict)
               array.write(toFile: self.plistFileParh, atomically: true)
               return index
           }
       }
       return 0
   }
   //修改属性列表中的数据
   public func modify(_ model: Note) -> Int{
       let array = NSMutableArray(contentsOfFile: self.plistFileParh)!
       var index = 0
       for item in array {
           let dict = item as!NSDictionary
           let strDate = dict.value(forKey: "date")as!String
           let date = dateFormatter.date(from: strDate)!
           index += 1
           //比较日期主键是否相等
           if date == model.date as Date{
               dict.setValue(model.content, forKey: "content")
               array.write(toFile: self.plistFileParh, atomically: true)
               return index
           }
       }
       return 0
   }
   //查询属性列表中所有数据模型数组
   public func findAll() -> NSArray{
       let array = NSMutableArray(contentsOfFile: self.plistFileParh)!
       let listData = NSMutableArray()
       for item in array {
           let dict = item as! NSDictionary
           let strDate = dict.value(forKey: "date")as!String
           let date = dateFormatter.date(from: strDate)!
           let content = dict.value(forKey: "content") as! String
           let note = Note(date: date, content: content)
           listData.add(note)
       }
       return listData.copy() as! NSArray
   }
   //按照时间主键查询数据
   public func findById(_ model: Note) -> Note?{
       let array = NSMutableArray(contentsOfFile: self.plistFileParh)!
       for item in array {
           let dict = item as! NSDictionary
           let strDate = dict.value(forKey: "date")as!String
           let date = dateFormatter.date(from: strDate)!
           let content = dict.value(forKey: "content") as! String
           if date == model.date as Date{
               let note = Note(date: date, content: content)
               return note
           }
       }
       return nil
   }
}

SQLite数据库

  • SQLite是嵌入式系统使用的关系数据库,目前主流的版本是SQLite 3。采用C语言编写,具有可移植性强、可靠性高、小而易用的特点。SQLite 运行时与使用它的应用程序之间共用相同的进程空间,而不是单独的两个进程。
  • SQLite提供了对SQL-92标准的支持,支持多表、索引、事物、视图和触发。SQLite是无数据类型数据库。
  • 虽然SQLite可以忽略数据类型,但从编程规范上讲,我们还是应该在创建表的时候指定数据类型。常见的数据类型有:
    INTEGER:有符号整数类型
    REAL:浮点类型
    TEXT:字符串类型
    BLOB:二进制大对象类型,能够存放任何二进制数据
    SQLite没有布尔类型,可用0和1代替,也没有日期和时间类型。

iOS项目中使用SQLite

  • 添加SQLite3库:
    选择工程中TARGETS -> Build Phases -> Link Binary With Libraries -> +选择libsqlite3.0.tbdlibsqlite3.tbd添加
    添加SQLite数据库
  • 配置Swift环境:
    如果我们采用Swift语言开发,则会更加复杂。这是因为SQLite API是基于C语言的,Swift要想调用C语言API,则需要桥接头文件。具体可参考文章:Swift和OC混编。在桥接文件中导入#import "sqlite3.h"
  • 常见sqlite操作函数
OpaquePointer: *db,数据库句柄,跟文件句柄FIFL类似,这里是sqlite3指针;

sqlite3_stmt: *stmt,相当于ODBC的Command对象,用于保存编译好的SQL语句;

sqlite3_open(): 打开数据库,没有数据库时创建;

sqlite3_exec(): 执行非查询的SQL语句;

sqlite3_step(): 在调用sqlite3_prepare后,使用这个函数在记录集中移动;

sqlite3_close():关闭数据库文件;

sqlite3_column_text():取text类型的数据;

sqlite3_column_blob():取blob类型的数据;

sqlite3_column_int():取int类型的数据;
  • 创建数据库,分为3步:
    1. 使用sqlite3_open函数打开数据库
    2. 使用sqlite3_exec函数执行Create Table语句,创建数据表
    3. 使用sqlite3_close函数释放资源
import Foundation
//数据库名称
let DBFILE_NAME = "NoteList.sqlite3"
class NoteDAO_SQLite3{
   //如果指向的类型没有完全定义或不能在 Swift 中表示,这种指针将会被转换为 COpaquePointer (在 Swift 3.0 中,将会简化为 OpaquePointer ),一种没有类型的指针,特别是只包含一些位(bits)的结构体。 COpaquePointer 指向的值不能被直接访问,指针变量首先需要转换才能使用。
   private var db: OpaquePointer? = nil
   private var dateFormatter = DateFormatter()
   //数据库路径
   private var dbFilePath:String!
   
   public static let shareInstance: NoteDAO_SQLite3 = {
       let instance = NoteDAO_SQLite3()
       //数据库路劲
       instance.dbFilePath = instance.applicationDocumentsDirectoryFile
       instance.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
       //初始化数据库
       instance.createEditableCopyOfDatabaseIfNeeded()
       return instance
   }()
   //数据库路劲
   let applicationDocumentsDirectoryFile: String = {
       let documentDirectory: NSArray = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
       let basePath = documentDirectory.firstObject as!String
       let path = basePath + "/" + DBFILE_NAME
       return path
   }()
   //初始化文件
   private func createEditableCopyOfDatabaseIfNeeded(){
       let fileManager = FileManager.default
       //判断数据库是否已经存在
       let dbexits = fileManager.fileExists(atPath: self.dbFilePath)
       //如果已经数据库已经存在了就直接返回,不再创建了
       if dbexits{
           return
       }
       //转化为c语言字符串
       let cpath = self.dbFilePath.cString(using: String.Encoding.utf8)
       if sqlite3_open(cpath!, &db) != SQLITE_OK{
           print("打开数据库失败")
       }else{
           //数据库打开成功则创建表,并设置cdate为主键,content关键字为内容
           let sql = "CREATE TABLE IF NOT EXISTS Note(cdate TEXT PRIMARY KEY,content TEXT)"
           let cSql = sql.cString(using: String.Encoding.utf8)
           //执行sql语句
           if (sqlite3_exec(db, cSql!, nil, nil, nil) != SQLITE_OK){
               print("建表失败")
           }
       }
       sqlite3_close(db)
   }
}
  • 查询数据,主要有一下步骤:
    1. 使用sqlite3_open函数打开数据库
    2. 使用sqlite3_prepare_v2函数预处理SQL语句
    3. 使用sqlite3_bind_text函数绑定参数
    4. 使用sqlite3_step函数执行SQL语句,变量结果子集
    5. 使用sqlite3_column_text等函数提取字段数据
    6. 使用sqlite3_finalizesqlite3_close函数释放资源关闭数据库
 //查询数据
extension NoteDAO_SQLite3{
   //按照主键查询某一个数据
   public func findById(_ model: Note) -> Note?{
       //获取数据库路径C语言
       let cpath = self.dbFilePath.cString(using: String.Encoding.utf8)
       //打开数据库
       if sqlite3_open(cpath!, &db) != SQLITE_OK{
           print("数据库打开失败")
       }else{
           //where条件查询语句
           let sql = "SELECT cdate,content FROM Note where cdate = ?"
           let cSql = sql.cString(using: String.Encoding.utf8)
           var statement: OpaquePointer? = nil
           //预处理过程
           if sqlite3_prepare_v2(db, cSql!, -1, &statement, nil) == SQLITE_OK{
               //准备参数
               let strDate = self.dateFormatter.string(from: model.date as Date)
               let cDate = strDate.cString(using: String.Encoding.utf8)
               //绑定参数
               sqlite3_bind_text(statement, 1, cDate!, -1, nil)
               //执行查询
               if sqlite3_step(statement) == SQLITE_ROW{
                   //还有其他行没有遍历
                   let note = Note()
                   //读取字符串字段
                   if let strDate = getColumValue(index: 0, stmt: statement!){
                       //获取日期
                       let date: Date = self.dateFormatter.date(from: strDate)!
                       //给模型赋值
                       note.date = date
                   }
                   if let strContent = getColumValue(index: 1, stmt: statement!){
                       note.content = strContent
                   }
                   //释放资源
                   sqlite3_finalize(statement)
                   //关闭数据库
                   sqlite3_close(db)
                   return note
               }
           }
           //释放资源
           sqlite3_finalize(statement)
       }
       //关闭数据库
       sqlite3_close(db)
       return nil
   }
   //获取字段数据
   private func getColumValue(index:CInt, stmt:OpaquePointer)->String?{
       //UnsafeMutableRawPointer是指任意类型的c指针
       if let prt = UnsafeMutableRawPointer.init(mutating: sqlite3_column_text(stmt, index)){
           //将任意类型的c指针绑定到Char类型指针
           let uptr = prt.bindMemory(to:CChar.self,capacity:0)
           //将指针所指类容创建成String类型
           let txt = String(validatingUTF8: uptr)
           return txt
       }
       return nil
   }
   
   //查询所有数据
   public func findAll() -> Array<Any>{
       let cpath = self.dbFilePath.cString(using: String.Encoding.utf8)
       var listData = Array<Any>()
       if sqlite3_open(cpath!, &db) != SQLITE_OK{
           print("数据库打开失败")
       }else{
           //插入数据SQL语句
           let sql = "SELECT cdate ,content FRPM Note"
           let cSql = sql.cString(using: String.Encoding.utf8)
           var statement: OpaquePointer? = nil
           //预处理过程
           if sqlite3_prepare_v2(db, cSql!, -1, &statement, nil) == SQLITE_OK{
               //执行查询语句
               while sqlite3_step(statement) == SQLITE_ROW{//遍历查询结果集, SQLITE_ROW表示结果集中海油数据
                   let note = Note()
                   if let strDate = getColumValue(index: 0, stmt: statement!){
                       let date: Date = self.dateFormatter.date(from: strDate)!
                       note.date = date
                   }
                   if let strContent = getColumValue(index: 1, stmt: statement!){
                       note.content = strContent
                   }
                   listData.append(note)
               }
           }
           sqlite3_finalize(statement)
       }
       sqlite3_close(db)
       return listData
   }
}
  • 修改数据,主要有一下步骤:
    1. 使用sqlite3_open函数打开数据库
    2. 使用sqlite3_prepare_v2函数预处理SQL语句
    3. 使用sqlite3_bind_text函数绑定参数
    4. 使用sqlite3_step函数执行SQL语句
    5. 使用sqlite3_finalizesqlite3_close函数释放资源关闭数据库
   //修改数据
extension NoteDAO_SQLite3{
    //添加数据
    public func create(_ model: Note){
        //获取数据库路径C语言
        let cpath = self.dbFilePath.cString(using: String.Encoding.utf8)
        if sqlite3_open(cpath!, &db) != SQLITE_OK{
            print("数据库打开失败")
        }else{
            //插入数据SQL语句
            let sql = "INSERT OR REPLACE INTO note (cdate, content) VALUE(?,?);"
            let cSql = sql.cString(using: String.Encoding.utf8)
            var statement: OpaquePointer? = nil
            //预处理过程
            if sqlite3_prepare_v2(db, cSql!, -1, &statement, nil) == SQLITE_OK{
                //准备参数
                let strDate = self.dateFormatter.string(from: model.date as Date)
                let cDate = strDate.cString(using: String.Encoding.utf8)
                let cConetent = model.content.cString(using: String.Encoding.utf8)
                //绑定参数
                sqlite3_bind_text(statement, 1, cDate!, -1, nil)
                sqlite3_bind_text(statement, 2, cConetent!, -1, nil)
                //执行插入数据SQL语句
                if sqlite3_step(statement) != SQLITE_DONE{//判断是否执行完成
                    print("插入数据失败")
                }
            }
            sqlite3_finalize(statement)
        }
        sqlite3_close(db)
    }
    //删除数据
    public func remove(_ model: Note){
            //获取数据库路径C语言
            let cpath = self.dbFilePath.cString(using: String.Encoding.utf8)
            if sqlite3_open(cpath!, &db) != SQLITE_OK{
                print("数据库打开失败")
            }else{
                //根据日期时间主键删除数据SQL语句
                let sql = "DELETE from note where cdate = ?"
                let cSql = sql.cString(using: String.Encoding.utf8)
                var statement: OpaquePointer? = nil
                //预处理过程
                if sqlite3_prepare_v2(db, cSql!, -1, &statement, nil) == SQLITE_OK{
                    //准备参数
                    let strDate = self.dateFormatter.string(from: model.date as Date)
                    let cDate = strDate.cString(using: String.Encoding.utf8)
                    //绑定参数
                    sqlite3_bind_text(statement, 1, cDate!, -1, nil)
                    //执行SQL语句
                    if sqlite3_step(statement) != SQLITE_DONE{//判断是否执行完成
                        print("插入数据失败")
                    }
                }
                sqlite3_finalize(statement)
            }
        sqlite3_close(db)
    }
    
    //修改数据
    public func modify(_ model: Note){
        //获取数据库路径C语言
        let cpath = self.dbFilePath.cString(using: String.Encoding.utf8)
        if sqlite3_open(cpath!, &db) != SQLITE_OK{
            print("数据库打开失败")
        }else{
            //根据日期时间主键跟新内容的SQL语句
            let sql = "UPDATE note set content =? where cdate =?"
            let cSql = sql.cString(using: String.Encoding.utf8)
            var statement: OpaquePointer? = nil
            //预处理过程
            if sqlite3_prepare_v2(db, cSql!, -1, &statement, nil) == SQLITE_OK{
                //准备参数
                let strDate = self.dateFormatter.string(from: model.date as Date)
                let cDate = strDate.cString(using: String.Encoding.utf8)
                let cConetent = model.content.cString(using: String.Encoding.utf8)
                //绑定参数
                sqlite3_bind_text(statement, 1, cDate!, -1, nil)
                sqlite3_bind_text(statement, 2, cConetent!, -1, nil)
                //执行SQL语句
                if sqlite3_step(statement) != SQLITE_DONE{//判断是否执行完成
                    print("插入数据失败")
                }
            }
            sqlite3_finalize(statement)
        }
        sqlite3_close(db)
    }
}

CoreData

  • CoreData出现在iOS 3中,是苹果为macOS和iOS系统应用开发提供的对象关系映射技术(ORM)。它基于高级数据持久化API,其底层最终是SQLite数据库、二进制文件和内存数据保存,使开发人员不用再关系数据储存细节问题,不用再使用SQL语句,不用面对SQLite的C语言函数。
  • 实体在关系模型中代表表的一条数据,该表描述了实体的结构有哪些属性和关系。实体在对象模型中代表类的一个对象,类描述了实体的结构,实体是类的对象。因此表是与类对应的概念,记录是与对象对应的概念
    关系型数据库

CoreData的具体使用步骤

1. 添加CoreData:

  1. 创建项目时,Xcode有两种工程模板(Master-Detail Application和Single View Application)可以直添加CoreData支持。勾选中Use Core Data即可。
    CoreData
    创建完后会自动生成工程名.xcdatamodeld文件(它是模型文件可利用它可视化地设计数据库,生成实体类代码和SQLite数据文件),并在AppDelegate生成创建Core Data栈和数据保存的方法代码(iOS以前要自己去手写),
import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
// MARK: - Core Data stack  创建Core Data栈
    //NSPersistentContainer 是iOS 10 新增加的类,称为持久化容器,用于简化之前的Core Data栈创建过程;iOS 10之前需要我们手动创建Core Data栈
    //storeDescription 是NSPersistentStoreDescription类型用来描述配置信息
    lazy var persistentContainer: NSPersistentContainer = {
        //参数name是容器的名字,它也是放到MainBundle资源目录中模型文件的名字
        let container = NSPersistentContainer(name: "Core_Data")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

    // MARK: - Core Data Saving support   Core Data数据保存方法
    func saveContext () {
        let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }
}
  1. 在已有项目中创建CoreData。
    File->New->File->Core Data->Data Model
    Data Model
  • Core Data 实现数据持久化是通过Core Data栈。它有一个或多个被管理的对象上下文,连接到一个持久化存储协调器,一个持久化存储协调器连接到一个或多个持久化对象存储,持久化对象存储与底层存储文件关联。一个持久化存储协调器也可以管理多个被管理对象模型。一个持久化存储协调器就意味着一个Core Data栈,我们可以实现数据的增、删、改、查。
    Core Data栈
  1. 被管理对象上下文(MOC):在被管理对象上下文中可以查找、删除、和插入对象,然后通过栈同步到持久化对象存储。对应类是NSManagedObjectContext
  2. 持久化存储协调器(PSC):在持久化对象存储智商提供一个接口,我们可以把他理解成数据库连接。对相应类是NSPersistentyStoreCoordinator
  3. 持久化对象存储(POS):执行所有底层的从对象到数据的转换,并负责打开和关闭数据文件。有3种持久化实现方式:SQLite、二进制文件和内存形式。
  4. 被管理对象模型(MOM): 是系统中的实体,与数据中的表等对象对应。对应类是NSManagedObjectModel

2. 添加生成实体

  • 添加实体:选中.xcdatamodeld文件,添加实体(表),修改实体名(表名),添加字段(属性)。
    添加实体
  • 生成实体:先绑定Core Data栈管理的对象。选择Editor -> Create NSManagedObject Subclass...,然后选择对应的模型文件,选择对应的实体表文件。会生成两个swift文件:NoteManagedObject+CoreDataClass.swift(定义了NoteManagedObject类,它是被Core Data管理的对象),NoteManagedObject+CoreDataProperties.swift(是NoteManagedObject的扩展)
    绑定管理对象
    在这里插入图片描述
    选择模型文件
    选择表文件
    注意1:可能会产生错误Invalid redeclaration of 'NoteManagedObject'
    在这里插入图片描述
    解决方案:在Create NSManagedObject Subclass...之前要设置CodegenManua/None
    解决方案
    注意2:默认情况下,模型文件生成的代码都是Swift语言,如果OC项目想生成OC语言,必须选中工程名.xcdatamodeld然后设置Code Generation -> LanguageObjective-C,这里本身是Swift示例则不用修改。
    模型文件生成的代码都是Swift语言

3. 创建CoreData栈DAO

//CoreDataDAO类似于Xcode自动生成的CoreData栈代码

import Foundation
import CoreData
//定义一个操作CoreData的类
class CoreDataDAO: NSObject {
    //懒加载持久化存储器属性
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "Model")
        container.loadPersistentStores { (storDes, error) in
            if let error = error as NSError?{
                print("持久化存储器错误:",error.localizedDescription)
            }
        }
        return container
    }()
    
    //保存数据
    func  saveContext(){
        let context = persistentContainer.viewContext
        if context.hasChanges {
            do{
                try context.save()
            }catch{
                let nserror = error as NSError
                print("数据保存错误:",nserror.localizedDescription)
            }
        }
    }
    
}

4. 创建CoreDataDAO子类,添加NoteManagedObject被管理的实体类

  • NoteCoreDataDAO继承NoteCoreDataDAO,对数据的增删改查都在这里进行。
import UIKit
import CoreData
//继承于CoreDataDAO
class NoteCoreDataDAO: CoreDataDAO {
//    var context : NSManagedObjectContext!
    public static let shareInstance: NoteCoreDataDAO = {
        let instance = NoteCoreDataDAO()
        //1.创建模型对象
        //获取模型路径
        let url = Bundle.main.url(forResource: "Model", withExtension: "momd")
        //根据模型文件创建模型对象
        let model = NSManagedObjectModel(contentsOf: url!)
        //2.创建持久化存储助理:数据库
        //利用模型对象创建助理对象
        let store = NSPersistentStoreCoordinator(managedObjectModel: model!)
        let documentDirectory: NSArray = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
        let basePath = documentDirectory.firstObject as!String
        //数据库路径
        let dbFilePath = basePath + "/" + "coreData.sqlite"
        let dbUrl = NSURL(fileURLWithPath: dbFilePath)
        //设置数据库相关信息 添加一个持久化存储库并设置存储类型和路径,NSSQLiteStoreType:SQLite作为存储库
        do{
            try store.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: dbUrl as URL, options: nil)
        }catch{
            let nserror = error as NSError
            print("数据保存错误:",nserror.localizedDescription)
        }
        
//        instance.context = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.mainQueueConcurrencyType)
//        //关联持久化助理
//        instance.context.persistentStoreCoordinator = store
        return instance
    }()
}
//查询数据
extension NoteCoreDataDAO {
    //查询所有数据(无条件查询)
    public func findAll() -> NSMutableArray {
        let context = persistentContainer.viewContext
        //NSEntityDescription是实体关联的描述类,通过制定实体名字获得NSEntityDescription实例对象
        let entity = NSEntityDescription.entity(forEntityName: "Note", in: context)
        //NSFetchRequest是数据提取请求类,用于查询
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
        //把实体描述设置到请求对象
        fetchRequest.entity = entity
        //NSSortDescriptor是排序描述类,可以指定排序字段和排序方式
        let sortDes = NSSortDescriptor(key: "date", ascending: true)
        //把排序描述对象的数组赋值给请求对象中
        fetchRequest.sortDescriptors = [sortDes]
        let resListData = NSMutableArray()
        do{
            //根据请求对象执行查询,返回数组集合,数据集合中放置的是被管理的NoteManagedObject实例对象
            let listData = try context.fetch(fetchRequest)
            for item in listData {
                let mo = item as! NoteManagedObject
                //转换成Note实例对象
                let note = Note(date: mo.date! as Date, content:mo.content!)
                resListData.add(note)
            }
        }catch{
            print("查询数据失败")
        }
        return resListData
    }
    
    //查询单个数据(有条件查询)
    public func findById(_ model: Note) -> Note?{
        let context =  persistentContainer.viewContext
        //NSEntityDescription是实体关联的描述类,通过制定实体名字获得NSEntityDescription实例对象
        let entity = NSEntityDescription.entity(forEntityName: "Note", in: context)
        //NSFetchRequest是数据提取请求类,用于查询
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
        //把实体描述设置到请求对象
        fetchRequest.entity = entity
        //NSPredicate是谓词,用于过滤对象
        fetchRequest.predicate = NSPredicate(format: "date = %@", model.date as CVarArg)
        do{
            let listData = try context.fetch(fetchRequest)
            if listData.count > 0 {
                let mo = listData[0] as! NoteManagedObject
                let note = Note(date: mo.date! as Date, content: mo.content!)
                return note
            }
            
        }catch{
            print("查询数据失败")
        }
         return nil
    }
    
}
//修改数据(增删改)
extension NoteCoreDataDAO{
    //插入数据
    public func create(_ model: Note){
        let context =  persistentContainer.viewContext
        //创建一个被管理的Note实例对象,返回值类型是NSManagedObject,本例中是NoteManagedObject,继承于NSManagedObject
        let note = NSEntityDescription.insertNewObject(forEntityName: "Note", into: context)as! NoteManagedObject
        note.date = model.date as NSDate
        note.content = model.content
       //保存数据
        self.saveContext()
    }
    // 删除数据
    public func remove(_ model: Note){
        let context =  persistentContainer.viewContext
        let entity = NSEntityDescription.entity(forEntityName: "Note", in: context)
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
        fetchRequest.entity = entity
        fetchRequest.predicate = NSPredicate(format: "date = %@", model.date as CVarArg)
        do {
            let listData =  try context.fetch(fetchRequest)
            if listData.count > 0{
                let note = listData[0] as! NSManagedObject
                //删除实体
                context.delete(note)
                self.saveContext()
            }
        } catch {
            print("删除数据失败")
        }
    }
    //修改数据
    public func modify(_ model: Note){
        let context =  persistentContainer.viewContext
        let entity = NSEntityDescription.entity(forEntityName: "Note", in: context)
        let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
        fetchRequest.entity = entity
        fetchRequest.predicate = NSPredicate(format: "date = %@", model.date as CVarArg)
        do {
            let listData =  try context.fetch(fetchRequest)
            if listData.count > 0{
                let note = listData[0] as! NoteManagedObject
                note.content = model.content
                self.saveContext()
            }
        } catch {
            print("修改数据失败")
        }
    }
}
类名说明
CoreDataDAODAO基类
NoteCoreDataDAONoteCoreDataDAO类用于对数据的增删改查
Note未被管理的实体类
NoteManagedObject被管理的实体类

NoteManagedObject对象必须被严格限定值持久层中使用,不能出现在表示层和业务逻辑层,而Note对象则可以出现在表示层、业务逻辑层和持久层中。因此在持久层中将CoreData栈查询出NoteManagedObject数据转换为Note对象,返回给表示层和业务逻辑层。这个工作看起来比较麻烦,但基于CoreData实现的分层架构必须遵守这个规范。


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值