【ios笔记】CoreData

参考

官方文档-API
官方文档-CoreData编程指南(快速入门)
官方文档-Predicate编程指南(查询语句)
简书上的小例子(快速入门)
Core Data性能优化及多上下文操作
初识Core Data(4):批量操作、聚合操作、WWDC 2015新特性

概念

1.基础

  • CoreData是对Sqlite的封装,提供上层Api操作数据库
  • CoreDataStack:包括NSManagedObjectModel,NSPersistentStoreCoordinator,NSManagedObjectContext
  • NSPersistentContainer管理CoreDataStack

2. NSPersistentContainer

  • 简介:负责创建和管理NSManagedObjectModel,NSPersistentStoreCoordinator,NSManagedObjectContext。创建NSPersistentContainer并调用loadPersistentStores后就同时创建了以上三个实例,所以通过container可以获取上面3个实例的全部内容。一个app实例共享一个container,因此可以将container放入AppDelegate中。
  • 相关属性和方法
属性或方法解释
NSPersistentContainer(name: “TestCoreData”)初始化container,name为TestCoreData.xcdatamodeld的文件名
container.persistentStoreDescriptions返回(type: SQLite, url: xxx.sqlite)底层为sqlite储存,url为sqlite文件储存路径
container.loadPersistentStores { (description, err) in }指示加载sqlite文件信息到内存,即初始化CoreDataStack,不加载具体数据行
container.managedObjectModel获取model实例
container.persistentStoreCoordinator获取coordinator实例
NSPersistentContainer.defaultDirectoryURL()sqlite文件的默认储存路径
container.viewContext主队列的context
container.newBackgroundContext()创建新的后台context

3.NSManagedObjectModel

  • 简介:.xcdatamodeld描述对象的文件的程序化表示,即NSManagedObjectModel的实例代表文件中描述的全部内容,包括entities(所有实体)
  • 相关属性和方法
属性或方法解释
model.entities返回NSEntityDescription数组,即在.xcdatamodeld中的每个实体描述
model.entitiesByName返回字典数组,key为实体名,值为NSEntityDescription
  • NSEntityDesription
    实体描述
//获取实例
model.entitiesByName["实体名"]
//或者
NSEntityDescription.entity(forEntityName: "实体名", in: context) 
  • NSManagedObject
    实体的实例
//初始化,以User为例
let user = NSEntityDescription.insertNewObject(forEntityName: "User", into: context) as! User
//或者
let user = User(entity: objectModel.entitiesByName["User"]!, insertInto: context)

4.NSManagedObjectContext

属性或方法解释
fetch(NSFetchRequest)获取符合条件的对象数组
count(NSFetchRequest)符合条件的对象数组的大小
registeredObject(NSManagedObjectID)通过id获取对象
object(NSManagedObjectID)通过id获取对象
existingObject(NSManagedObjectID)通过id获取对象
registeredObjects(Set<NSManagedObject>)把对象集合注册到context
insertedObjects已插入,还未保存的对象
updatedObjects已提交更新,还未保存的对象
deletedObjects已提交删除,还未保存的对象
insert((NSManagedObject))插入
delete(NSManagedObject)删除
save()提交更变
hasChanges是否有未提交的更变
undo()撤销未提交的更改
redo()撤销undo
reset()将context恢复为基本状态
rollback()回滚,放弃所有插入和删除操作
perform(){}在context队列中异步执行块
performAndWait(){}在context队列中同步执行块

5.NSPersistentStoreCoordinator

  • 帮助contextpersistentStore(sqlite文件)通信
  • 应用:使用预置sqlite数据文件

//1.将存有数据的sqlite数据库文件放入项目中
//2.替换persistentStoreDescriptions
container.persistentStoreDescriptions=[NSPersistentStoreDescription(url: Bundle.main.url (forResource: "文件名", withExtension: "sqlite")!)]
//3.加载预置的sqlite文件
container.loadPersistentStores()
            { (description, err) in
                print("load description:\(description)")
                print("load err:\(err.debugDescription)")
        }

使用CoreData

1.创建CoreData模型

  • 新建DataModel文件
    – 命名为项目名.xcdatamodeld
  • 添加entity
    – Add Entity
  • 添加属性
    – 添加完属性后在entity的inspector页面添加Constrains主键
  • 添加关系
    – 如果该实体包含另一个实体,则可以在relationships中添加
  • 生成代码
    – 自动生成
    在entity的inspector页面,codegen中选中Class Definition,这样xcode将会自动生成NSManagedObjectModel代码,直接使用即可,不用再手动生成
    – 手动生成
    也可以选中Manual/None,这样需要点击Editor->create NSManagedObject Subclass手动生成代码
//以User的CURD为例
1.新建项目TestCoreData
2.新建文件TestCoreData.xcdatamodeld
3.添加entity:User
4.添加属性:id,integer64;name,String
5.User 的codegen为ClassDefinition,Constrains 为id

在这里插入图片描述

2.初始化NSPersistentContainer

//在AppDelegate.swift中,全局化初始NSPersistentContainer
lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "TestCoreData")
        container.loadPersistentStores(completionHandler: { storeDescription, error in
            print ("storeDescription:\(storeDescription)")
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

//在页面中获取container的引用
(UIApplication.shared.delegate as! AppDelegate).persistentContainer

在这里插入图片描述
在这里插入图片描述

3.增删改查

//step1.创建NSManagedObject
//方法一:
let user = NSEntityDescription.insertNewObject(forEntityName: "User", into: context) as! User
//方法二:
let user = User(entity: objectModel.entitiesByName["User"]!, insertInto: context)

//step2.赋值
user.id = 1
user.name = "a"

//step3.提交更改
do{
	try context.save()
}catch{
	print("save error:\(error)")
}
//查询user的name为a的记录
let fetchReq = NSFetchRequest<User>(entityName: "User")
        fetchReq.predicate = NSPredicate(format: "name==%@", "a")
        do {
            let users = try context.fetch(fetchReq)
            for user in users {
                print(user.toStr())
            }
        } catch{
            print("fetch error:\(error)")
        }
  • 更新
//把name为b的修改为c
let fetchReq = NSFetchRequest<User>(entityName: "User")
        fetchReq.predicate = NSPredicate(format: "name==%@", "b")
        do {
            let users = try context.fetch(fetchReq)
            for user in users {
                user.name = "c"
            }
            try context.save()
        } catch{
            print("error:\(error)")
        }
  • 删除
//删除name为b的全部记录
let fetchReq = NSFetchRequest<User>(entityName: "User")
        fetchReq.predicate = NSPredicate(format: "name==%@", "b")
        do {
            let users = try context.fetch(fetchReq)
            for user in users {
                context.delete(user)
            }
            try context.save()
        } catch{
            print("error:\(error)")
        }

4.更多查询用法

  • NSFetchRequest
    – 初始化
//以User为例
//这里指定NSFetchRequestResultType为User,即NSManagedObject
let fetchReq = NSFetchRequest<User>(entityName: "User")
//返回类型还可以指定为NSNumber,NSDictionary,NSManagedObjectID

– 指定fetch限制

属性含义
predicate指定predicate
fetchLimit提取数量
fetchOffset跳过多少开始获取
fetchBatchSize批量大小
affectedStores指定特定的sqlite
//获取name为"a",从第2个开始,的3个User
let fetchReq = NSFetchRequest<User>(entityName: "User")
fetchReq.predicate = NSPredicate(format: "name == %@","a")
fetchReq.fetchLimit = 3
fetchReq.fetchOffset = 2
  • 结果排序
//按name升序,name相同时按id降序
fetchReq.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true),NSSortDescriptor(key: "id", ascending: false)]
//可以自定义比较器实现自定义排序
init(key: String?, ascending: Bool, comparator: Comparator)
  • fetch指定字段和Distinct
//返回特定的字段
//例只返回所有user的name,注意返回类型为NSDictionary,.dictionaryResultType
let fetchReq = NSFetchRequest<NSDictionary>(entityName: "User")
fetchReq.resultType = .dictionaryResultType
fetchReq.propertiesToFetch = ["name"]

//返回唯一值,这里的唯一值是指propertiesToFetch中指定的所有字段相同的记录
fetchReq.returnsDistinctResults = true
//
//以name分组,并得到每组的数量
//select count(*),name from user group by name
let countExp = NSExpressionDescription()
        countExp.name = "count"
        countExp.expression = NSExpression(forFunction: "count:", arguments: [NSExpression(forKeyPath: "name")])
        fetchReq.propertiesToFetch = ["name",countExp]
        fetchReq.propertiesToGroupBy = ["name"]
//having count>=2
//参考:https://stackoverflow.com/questions/23116629/is-it-possible-to-use-a-group-by-count-in-the-havingpredicate-for-a-coredata-fet
fetchReq.havingPredicate = NSPredicate(format: "%@ >=2", NSExpression(forVariable: "count"))

Predicate编程

1.创建Predicate

  • 使用格式化字符串创建
//name = b
NSPredicate(format: "name==%@", "b")
//name 以 b开头
//不使用参数,需要用\转义
NSPredicate(format: "name like \"b*\"")

//不能像下面这样使用
NSPredicate(format: "name like %@*","b")//这样会格式化为name like "b"*
//正确的使用方法为
NSPredicate(format:"name like %@","b*")

//如果属性也是动态的,则需要使用%K
NSPredicate(format:"%K like %@","name","b*")
  • 直接使用代码创建
    这样创建书写代码很多,但很容易懂
//例:创建表达式 id>=5 and id<8

//先创建id>=5表达式:这是个比较类型的表达式所以用NSComparisonPredicate,leftExpression是表达式左边的内容id,这是个属性字段,需要用forKeyPath来创建,rightExpression是表达式右边的内容5,是个常量,用forConstantValue,modifier为direct直接比较,type为greaterThanOrEqualTo大于等于,options为额外的选项,比如.caseInsensitive忽略大小写,这里用.normalized为没有额外的选项.
let greaterThanOrEqualTo = NSComparisonPredicate(leftExpression: NSExpression(forKeyPath: "id"), rightExpression: NSExpression(forConstantValue: 5), modifier: .direct, type: .greaterThanOrEqualTo, options: .normalized)
//创建id<8表达式
let lessThan = NSComparisonPredicate(leftExpression: NSExpression(forKeyPath: "id"), rightExpression: NSExpression(forConstantValue: 8), modifier: .direct, type: .lessThan, options: .normalized)
//NSCompoundPredicate组合上面两个表达式,andPredicateWithSubpredicates表示用And连接
fetchReq.predicate =NSCompoundPredicate(andPredicateWithSubpredicates: [greaterThanOrEqualTo,lessThan])
  • 从Predicate模板创建
//定义模板,$符号的变量是需要替换的变量
let template = NSPredicate(format: "name like $name")
//使用模板
//用b*代替$name
fetchReq.predicate = template.withSubstitutionVariables(["name":"b*"])

2.使用Predicate

  • 评估evalute
let predicate = NSPredicate(format: "name like %@", "a*")
print(predicate.evaluate(with: ["name":"abc"]))//true
  • 过滤数组
print(NSArray(array: ["abc","acd","bcd"]).filtered(using: NSPredicate(format: "SELF like %@", "a*")))
//[abc, acd]

3.字符串语法

符号解释例子
%@对象值(字符串,数字,日期等)NSPredicate(format: “name like %@ and id==%@”,“a*”,NSNumber(2))
%Kkey路径NSPredicate(format: “%K like %@”,“name”,“b*”)
=,==
>=
<=
>
<
!=
BETWEEN位于区间NSPredicate(format: “id BETWEEN {%d,%d}”,2,5)
AND,&&
OR,||
NOT ,!
[c]不区分大小写NSPredicate(format: “name BEGINSWITH[c] %@”,“B”)
BEGINSWITH字符串以什么开头
CONTAINS包含
ENDWITH以什么结尾
LIKE简单匹配,?一个字符,*0个或多个
MATCHES正则表达式匹配
IN在集合中NSPredicate(format: “name IN %@”,[“g”,“h”])
FALSE,NOfalse
TRUE,YEStrue
SELF要评估的对象本身

多线程

1.异步请求

NSAsynchronousFetchRequest

let fetchReq = NSFetchRequest<User>(entityName: "User")
let asyncReq = NSAsynchronousFetchRequest(fetchRequest: fetchReq) { result in
	print(result.finalResult)
}

2.后台任务

unFinished
//todo:先看UI相关内容,再来更新这里

//新建任务上下文
let taskContext = container.newBackgroundContext()
//在任务上下文中执行IO操作
taskContainer.performAndWait{
	//...
}
//任务context的结果合并到viewContext
container.viewContext.automaticallyMergesChangesFromParent = true

批量处理

1.批量更新

NSBatchUpdateRequest
直接批量更新数据,无需先把数据查询出来到内存

//把id>3的所有记录的name改为hhh
let bur = NSBatchUpdateRequest(entityName: "User")
        bur.propertiesToUpdate = ["name":"hhh"]
        bur.predicate = NSPredicate(format: "id > 3")
        bur.includesSubentities = true
        bur.resultType = .updatedObjectsCountResultType
        do {
            let result = try context.execute(bur) as! NSBatchUpdateResult
            print(result.result)
        } catch {
            print("error:\(error)")
        }
//返回类型有3种
//1.statusOnlyResultType只返回状态码,1为成功
//2.updatedObjectIDsResultType,返回更新了的objectId
//3.updatedObjectsCountResultType,返回更新了的记录数量

2.批量删除

与批量更新概念类似,初始化有一些区别

//1.创建批量删除请求,两种方式,
//第一种,通过构造查询请求,放入批量删除请求中,这样也是直接删除的,没有将查询请求的结果放入内存
init(fetchRequest: NSFetchRequest<NSFetchRequestResult>)
//第二种,放入需要删除的managedObjectId数组
init(objectIDs: [NSManagedObjectID])

//2.配置返回类型,与批量查询类似,3种返回类型
batchDeleteReq.resultType

3.批量插入

let bir = NSBatchInsertRequest(entityName: "User", objects: [["id":10,"name":"P"],["id":11,"name":"Q"]])
        bir.resultType = .statusOnly
        do {
            let result = try context.execute(bir) as! NSBatchInsertResult
            print(result.result)
        } catch {
            print("error:\(error)")
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值