文章目录
参考
官方文档-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
//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)) |
%K | key路径 | 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,NO | false | |
TRUE,YES | true | |
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)")
}