關(guān)鍵詞: coreData關(guān)聯(lián)關(guān)系無法回顯, .relationshipKeyPathsForPrefetching, ***.xcdatamodeld - editor style
背景:最近用swift重玩coreData,略有收獲,怕日后忘記,故在此小計.另附demo一份,點擊->傳送門<-獲取
-
半路添加coreData
如果新建項目要使用coreData,則新建時勾選即可.但要是老項目準備啟用coreData呢,其實也不復(fù)雜,兩步即可
-
新建一個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方法即為數(shù)據(jù)庫存儲方法,拿git來比喻的話就是,context(persistentContainer)方法存取數(shù)據(jù)是在暫存區(qū)活動(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()
}
以確保程序在進入后臺是保存數(shù)據(jù).至此,半途添加coreData完畢.
-
coreData"替代"model
coreData可以當(dāng)做是model來用,
其中,entities看做類,attributes看做屬性.當(dāng)按箭頭選擇了響應(yīng)的Module和Codegen屬性后會自動生成實體類的swift文件,只不過這個文件在左側(cè)不顯示,但這個類實際是可以像正常類一樣被調(diào)用的
let teacher = Teacher(context:context)
teacher.id = 1
teacher.subject = "科目1"
另外,對于屬性的設(shè)置,如圖
- 如果未選中Optional,則這個屬性必須進行賦值才能被coreData存儲,否則save方法會報錯,也無法存儲.
- 屬性的類型若是整型,如interger 16,則意味這個屬性是16位整形數(shù),也就是0到65535,如果對其賦值大于這個數(shù),也是無法save的.
-
關(guān)聯(lián)關(guān)系
對于關(guān)聯(lián)關(guān)系,如圖
- type屬性設(shè)置一對多和一對一,如果設(shè)置了To Many(一對多)后,可以通過Count來對關(guān)聯(lián)數(shù)量做限制,比如minimum=1,maximun=3,則1個teacher實體最多對應(yīng)3個student,第4個student無法被關(guān)聯(lián).
- 關(guān)系的兩端的Delete Rule的如果設(shè)為deny,則在刪除兩個有關(guān)聯(lián)的實體時,會報錯,這個設(shè)計是用來保護數(shù)據(jù)的,避免刪除出錯.對于沒有影響的關(guān)系置為Nullify即可.
- 代碼添加關(guān)聯(lián)
func allStudentsToTeacherWhosId1() {
guard teacherArray.count != 0 else {
return print("先生成老師數(shù)據(jù)")
}
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()
}
其中如果是一對多的關(guān)系,則只能能夠用M1的方式處理,關(guān)系是個NSMutableOrderedSet(可變集合),所以只能用.add的方法添加.(筆者試過setValueForKey,array.append的方法,都無法添加,添加成功了也沒法報存)
一對一則用M2的方式處理,直接賦值即可.
4.如果對兩個實體a,b的關(guān)聯(lián)關(guān)系設(shè)置為:a一對多b,b一對一a,則在代碼中a添加b時,重復(fù)添加b無效,b只會屬于最后一次添加的a實例.而如果a,b的關(guān)聯(lián)關(guān)系設(shè)置為:a一對多b,b一對多a,則在代碼中a添加b時,可以重復(fù)添加b,所有a實例的都b.
- 讀取儲存的關(guān)聯(lián)關(guān)系
- 可以通過以下語句在讀取實體前預(yù)讀關(guān)聯(lián)關(guān)系
(筆者實測,不用加也能讀取,要是讀取不出來,也可以試試)
let request = Teacher.fetchRequest()
request.relationshipKeyPathsForPrefetching = ["teacherTeahStudents"]
- 對于添加成功的關(guān)聯(lián)關(guān)系,比如id=1老師對應(yīng)x學(xué)生y學(xué)生z學(xué)生,
儲存后如果只是讀出打印,則無法展示關(guān)聯(lián)數(shù)據(jù),如圖只是讀出打印
而如果是遍歷該關(guān)系并打印,則可以正常展示.如圖遍歷該關(guān)系并打印
對于這種現(xiàn)象的原因筆者并不確定,搜索后結(jié)合推測可能是,coreData的節(jié)約內(nèi)存優(yōu)點導(dǎo)致的,即,如果并非真正使用則不完全加載關(guān)系,只有當(dāng)真正使用關(guān)系時(比如遍歷)才加載.這個只是個推測,僅供參考.
- 最后,若有錯誤,懇請斧正。






