关键词: coreData关联关系无法回显, .relationshipKeyPathsForPrefetching, ***.xcdatamodeld - editor style
背景:最近用swift重玩coreData,略有收获,怕日后忘记,故在此小计.另附demo一份,点击->传送门<-获取
-
半路添加coreData
如果新建项目要使用coreData,则新建时勾选即可.但要是老项目准备启用coreData呢,其实也不复杂,两步即可
-
新建一个coreData,如图
- 在AppDelegate中添加以下代码
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "CoreDataTest")
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
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)")
}
}
}
效果如图
此处要注意:需要将NSPersistentContainer(name: "CoreDataTest")的name换成上方新建的***.xcdatamodeld的名字.
此处persistentContainer方法即操作coreData的容器方法,saveContext方法即为数据库存储方法,拿git来比喻的话就是,context(persistentContainer)方法存取数据是在暂存区活动(git add .) 而saveContext方法就是推远端(git remote)了.
另外需要SceneDelegate的sceneDidEnterBackground方法添加代码
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
// Save changes in the application's managed object context when the application transitions to the background.
(UIApplication.shared.delegate as? AppDelegate)?.saveContext()
}
以确保程序在进入后台是保存数据.至此,半途添加coreData完毕.
-
coreData"替代"model
coreData可以当做是model来用,
其中,entities看做类,attributes看做属性.当按箭头选择了响应的Module和Codegen属性后会自动生成实体类的swift文件,只不过这个文件在左侧不显示,但这个类实际是可以像正常类一样被调用的
let teacher = Teacher(context:context)
teacher.id = 1
teacher.subject = "科目1"
另外,对于属性的设置,如图- 如果未选中Optional,则这个属性必须进行赋值才能被coreData存储,否则save方法会报错,也无法存储.
- 属性的类型若是整型,如interger 16,则意味这个属性是16位整形数,也就是0到65535,如果对其赋值大于这个数,也是无法save的.
-
关联关系
对于关联关系,如图
- type属性设置一对多和一对一,如果设置了To Many(一对多)后,可以通过Count来对关联数量做限制,比如minimum=1,maximun=3,则1个teacher实体最多对应3个student,第4个student无法被关联.
- 关系的两端的Delete Rule的如果设为deny,则在删除两个有关联的实体时,会报错,这个设计是用来保护数据的,避免删除出错.对于没有影响的关系置为Nullify即可.
- 代码添加关联
func allStudentsToTeacherWhosId1() {
guard teacherArray.count != 0 else {
return print("先生成老师数据")
}
let teacher = teacherArray[0]
//M1:一对多
for student in studentArray {
if let teacherTeahStudentRelationship = teacher.teacherTeahStudents as? NSMutableOrderedSet {
teacherTeahStudentRelationship.add(student)
student.stuLearnFromTeacher = teacher
} else {
teacher.teacherTeahStudents = NSMutableOrderedSet(object: student)
}
}
//M2:一对一
let book = WorkBook(context: context)
book.name = "id1号老师的教案"
teacher.teacherUseWorkBook = book
saveData()
sleep(1)
getDataShow()
}
其中如果是一对多的关系,则只能能够用M1的方式处理,关系是个NSMutableOrderedSet(可变集合),所以只能用.add的方法添加.(笔者试过setValueForKey,array.append的方法,都无法添加,添加成功了也没法报存)
一对一则用M2的方式处理,直接赋值即可.
4.如果对两个实体a,b的关联关系设置为:a一对多b,b一对一a,则在代码中a添加b时,重复添加b无效,b只会属于最后一次添加的a实例.而如果a,b的关联关系设置为:a一对多b,b一对多a,则在代码中a添加b时,可以重复添加b,所有a实例的都b.
- 读取储存的关联关系
- 可以通过以下语句在读取实体前预读关联关系
(笔者实测,不用加也能读取,要是读取不出来,也可以试试)
let request = Teacher.fetchRequest()
request.relationshipKeyPathsForPrefetching = ["teacherTeahStudents"]
- 对于添加成功的关联关系,比如id=1老师对应x学生y学生z学生,
储存后如果只是读出打印,则无法展示关联数据,如图
而如果是遍历该关系并打印,则可以正常展示.如图
对于这种现象的原因笔者并不确定,搜索后结合推测可能是,coreData的节约内存优点导致的,即,如果并非真正使用则不完全加载关系,只有当真正使用关系时(比如遍历)才加载.这个只是个推测,仅供参考.
- 最后,若有错误,恳请斧正。