Swift之深入解析Core Data數(shù)據(jù)管理的集成指南
一、Core Data 簡介
① 什么是 Core Data?
CoreData 是 iOS SDK 里的一個(gè)很強(qiáng)大的框架,允許開發(fā)者以面向?qū)ο蟮姆绞酱鎯凸芾頂?shù)據(jù),使用 CoreData 框架,開發(fā)者可以輕松有效地通過面向?qū)ο蟮慕涌诠芾頂?shù)據(jù)。
CoreData 是一個(gè)模型層的技術(shù),可以幫助建立代表程序狀態(tài)的模型層。CoreData 也是一種持久化技術(shù),能將模型對象的狀態(tài)持久化到磁盤,但它最重要的特點(diǎn)是:不僅是一個(gè)加載和保存數(shù)據(jù)的框架,還能和內(nèi)存中的數(shù)據(jù)很好的共事。在數(shù)據(jù)操作過程中,Core Data 無需編寫任何 SQL 語句。
CoreData 使用包括實(shí)體和實(shí)體間的關(guān)系,以及查找符合某些條件實(shí)體的請求等內(nèi)容。開發(fā)者可以在純對象層上查找與管理這些數(shù)據(jù),而不必?fù)?dān)心存儲和查找的實(shí)現(xiàn)細(xì)節(jié)。
CoreData 框架最早出現(xiàn)在 Mac OS 10.4 Tiger 與 iOS 3.0 系統(tǒng),經(jīng)過成千上萬的應(yīng)用程序以及數(shù)以百萬用戶的反復(fù)驗(yàn)證,CoreData 確實(shí)已經(jīng)是一套非常成熟的框架,它利用 Objective-C 語言和運(yùn)行時(shí),巧妙地集成 CoreFoundation 框架,是一個(gè)易于使用的框架,不僅可以優(yōu)雅地管理對象圖,而且在內(nèi)存管理方面表現(xiàn)異常優(yōu)異。
CoreData 不是一個(gè)數(shù)據(jù)庫,不要用數(shù)據(jù)庫的眼光去看待 CoreData,它不是應(yīng)用程序的數(shù)據(jù)庫,也不是將數(shù)據(jù)持久化保存到數(shù)據(jù)庫的 API,它是一個(gè)用于管理對象圖的框架,可以把對象圖寫入磁盤從而持久化保存。
② Core Data 特點(diǎn)
支持對象改變過程管理,支持撤銷和重做;
關(guān)系維護(hù)(例如刪除一張照片的同時(shí),會(huì)在拍照者中刪除對應(yīng)的指針);
惰性加載,部分加載來降低內(nèi)存(在需要使用數(shù)據(jù)的時(shí)候再加載);
屬性值檢查(例如:保證年齡在0-200歲),保證數(shù)據(jù)有意義;
Schema 遷移:Schema 用來描述對象(例如:name,age,sex 描述一個(gè)人),CoreData 能夠適應(yīng) Schema 的改變;
NSFetchedResultsController 來更好支持 Tableview;
完整的支持 KVC/KVO;
支持復(fù)雜的數(shù)據(jù)查詢;
支持 Merge policies(例如:兩個(gè)線程對同一個(gè)數(shù)據(jù)改變的情況)。
③ Core Data 架構(gòu)
Core Data 的完整架構(gòu),如下圖所示:
大部分情況下的架構(gòu):
分析:
NSManagedObjectContext 可以理解為是一個(gè)容器,從持久化存儲(文件)中查詢的數(shù)據(jù)在這個(gè)容器中形成對象圖,對這些對象圖中的對象操作都會(huì)存儲在這個(gè)容器里,直到發(fā)出指令讓容器中的內(nèi)容同步到磁盤;
NSManagedObject 是 NSManagedObjectContext 對象圖中的實(shí)際對象,由 NSManagedObjectContext 管理,NSManagedObjectContext 會(huì)存儲這些對象的變化來支持重做和撤銷;
NSPersistent Store 負(fù)責(zé)把對象圖中的信息 map 到實(shí)際的存儲信息,NSPersistentStoreCordinator 存儲調(diào)度器:負(fù)責(zé)將數(shù)據(jù)保存到磁盤的;
SQLite 和 FileSystem 是保存到持久化存儲的文件,CoreData 支持 SQLite 的數(shù)據(jù)格式,但是需注意:coreData 不是 DBMS,并不能管理 SQLite。
④ Core Data 與應(yīng)用、磁盤存儲的關(guān)系
Core Data 比 SQLite 做了更進(jìn)一步的封裝,SQLite 提供了數(shù)據(jù)的存儲模型,并提供了一系列 API,可以通過 API 讀寫數(shù)據(jù)庫,去處理想要處理的數(shù)據(jù)。但是 SQLite 存儲的數(shù)據(jù)和編寫代碼中的數(shù)據(jù)(比如一個(gè)類的對象)并沒有內(nèi)置的聯(lián)系,必須我們自己編寫代碼去一一對應(yīng)。
而 Core Data 卻可以解決一個(gè)數(shù)據(jù)在持久化層和代碼層的一一對應(yīng)關(guān)系,也就是說,處理一個(gè)對象的數(shù)據(jù)后,通過保存接口,它可以自動(dòng)同步到持久化層里,而不需要去實(shí)現(xiàn)額外的代碼。這種“對象→持久化”方案叫“對象→關(guān)系映射”(英文簡稱 ORM)。
Core Data 還提供了很多有用的特性,比如回滾機(jī)制,數(shù)據(jù)校驗(yàn)等,它與應(yīng)用、磁盤存儲的關(guān)系如下:
二、數(shù)據(jù)模型
① 數(shù)據(jù)模型文件:Data Model
當(dāng)使用 Core Data 時(shí),需要一個(gè)用來存放數(shù)據(jù)模型的地方,數(shù)據(jù)模型文件就是要?jiǎng)?chuàng)建的文件類型,它的后綴是 .xcdatamodeld,在創(chuàng)建工程的時(shí)候,勾選 Use Core Data 創(chuàng)建:
或者在項(xiàng)目中選新建文件→Data Model 即可創(chuàng)建:
系統(tǒng)默認(rèn)提供的命名為 Model.xcdatamodeld,以 Model.xcdatamodeld 作為示例的文件名,這個(gè)文件就相當(dāng)于數(shù)據(jù)庫中的“庫”。通過編輯這個(gè)文件,就可以去添加定義想要處理的數(shù)據(jù)類型。
② 數(shù)據(jù)模型中的表格:Entity
當(dāng)在 Xcode 中點(diǎn)擊 Model.xcdatamodeld 時(shí),會(huì)看到蘋果提供的編輯視圖,其中有個(gè)醒目的按鈕 Add Entity:
什么是 Entity 呢?中文翻譯叫“實(shí)體”,如果把數(shù)據(jù)模型文件比作數(shù)據(jù)庫中的“庫”,那么 Entity 就相當(dāng)于庫里的“表格”。簡單理解,Entity 就是定義數(shù)據(jù)表格類型的名詞。例如,這個(gè)數(shù)據(jù)模型是用來存放圖書館信息的,那么很自然的,會(huì)想建立一個(gè)叫 Book 的 Entity。
注意:創(chuàng)建 Entity 實(shí)體的首字母必須為大寫。
③ 屬性 Attributes
當(dāng)建立一個(gè)名為 Book 的 Entity 時(shí),會(huì)看到視圖中有欄寫著 Attributes,我們知道,當(dāng)定義一本書時(shí),自然要定義書名、書的編碼等信息,這部分信息叫 Attributes,即書的屬性:
同理,也可以再添加一個(gè)讀者:Reader 的 Entity 描述,如下:
④ 關(guān)系 Relationship
在使用 Entity 編輯時(shí),除了看到 Attributes 一欄,還看到下面有 Relationships 一欄,這欄是做什么的?
回到示例中來,當(dāng)定義圖書館信息時(shí),剛書籍和讀者的信息,但這兩個(gè)信息彼此是孤立的,而事實(shí)上它們存在著聯(lián)系。比如一本書,它被某個(gè)讀者借走了,這樣的數(shù)據(jù)該怎么存儲呢?直觀的做法是再定義一張表格來處理這類關(guān)系,但是 Core Data 提供了更有效的辦法:Relationship。
從 Relationship 的思路來思考,當(dāng)一本書 A 被某個(gè)讀者 B 借走,可以理解為這本書 A 當(dāng)前的“借閱者”是該讀者 B,而讀者 B 的“持有書”是 A。從以上描述可以看出,Relationship 所描述的關(guān)系是雙向的,即 A 和 B 互相以某種方式形成了聯(lián)系,而這個(gè)方式是我們來定義的。
在 Reader 的 Relationship 下點(diǎn)擊 + 號鍵,然后在 Relationship 欄的名字上填 borrow,表示讀者和書的關(guān)系是“借閱”,在 Destination 欄選擇 Book,這樣,讀者和書籍的關(guān)系就確立了,如下所示:
對于第三欄,Inverse 卻沒有東西可以填,這是為什么?因?yàn)楝F(xiàn)在定義了讀者和書的關(guān)系,卻沒有定義書和讀者的關(guān)系。因?yàn)殛P(guān)系是雙向的,就好比定義了 A 是 B 的父親,那也要同時(shí)去定義 B 是 A 的兒子一個(gè)道理,計(jì)算機(jī)不會(huì)幫我們打理另一邊的聯(lián)系。
理解了這點(diǎn),開始選擇 Book 的一欄,在 Relationship 下添加新的 borrowBy,Destination 是 Reader,這時(shí)候點(diǎn)擊 Inverse 一欄,會(huì)發(fā)現(xiàn)彈出了borrowBy,直接點(diǎn)上,這是因?yàn)樵诙x Book 的 Relationship 之前,已經(jīng)定義了 Reader 的 Relationship 了,所以電腦已經(jīng)知道了讀者和書籍的關(guān)系,可以直接選上。而一旦選好了,那么在 Reader 的 Relationship 中,我們會(huì)發(fā)現(xiàn) Inverse 一欄會(huì)自動(dòng)補(bǔ)齊為 borrowBy,這是因?yàn)殡娔X這時(shí)候已經(jīng)完全理解了雙方的關(guān)系,自動(dòng)做了補(bǔ)齊。
⑤ “一對一”和“一對多”:to one 和 to many
建立 Reader 和 Book 之間的聯(lián)系的時(shí)候,發(fā)現(xiàn)它們的聯(lián)系邏輯之間還漏了一個(gè)環(huán)節(jié):假設(shè)一本書被一個(gè)讀者借走了,它就不能被另一個(gè)讀者借走,而當(dāng)一個(gè)讀者借書時(shí),卻可以借很多本書,也就是說,一本書只能對應(yīng)一個(gè)讀者,而一個(gè)讀者卻可以對應(yīng)多本書,這就是“一對一→to one”和“一對多→to many”。
Core Data 允許配置這種聯(lián)系,具體做法就是在 RelationShip 欄點(diǎn)擊對應(yīng)的關(guān)系欄,它將會(huì)出現(xiàn)在右側(cè)的欄目中(欄目如果沒出現(xiàn)可以在 Xcode 右上角的按鈕調(diào)出,如果點(diǎn)擊后欄目沒出現(xiàn) Relationship 配置項(xiàng),可以多點(diǎn)擊幾下,這是 Xcode 的 bug)。
在 Relationship 的配置項(xiàng)里,有一項(xiàng)項(xiàng)名為 Type,點(diǎn)擊后有兩個(gè)選項(xiàng),一個(gè)是 To One(默認(rèn)值),另一個(gè)就是 To Many。
Book 與 Reader 的 Relationship 如下:
Reader 與 Book 的 Relationship 如下:
通過改變實(shí)體的展示樣式,能夠幫助我們更加直觀的看到它們之間的關(guān)系:
三、Core Data 的主倉庫
① 主倉庫 NSPersistentContainer 說明
當(dāng)配置完 Core Data 的數(shù)據(jù)類型信息后,并沒有產(chǎn)生任何數(shù)據(jù),就好比圖書館已經(jīng)制定了圖書的規(guī)范:一本書應(yīng)該有名字、isbm、頁數(shù)等信息,規(guī)范雖然制定,卻沒有真的引進(jìn)書進(jìn)來,那么怎么才能產(chǎn)生和處理數(shù)據(jù)呢?這就需要通過代碼真刀真槍的和 Core Data 打交道了。
由于 Core Data 的功能較為強(qiáng)大,必須分成多個(gè)類來處理各種邏輯,一次性學(xué)習(xí)多個(gè)類是不容易的,還容易混淆。要和這些各司其職的類打交道,不得不提第一個(gè)要介紹的類 NSPersistentContainer,因?yàn)樗褪谴娣胚@多個(gè)類成員的“倉庫類”。
這個(gè) NSPersistentContainer 就是通過代碼和 Core Data 打交道的第一個(gè)目標(biāo),它存放著幾種和 Core Data 進(jìn)行業(yè)務(wù)處理的工具,當(dāng)拿到這些工具之后,就可以自由的訪問數(shù)據(jù),所以它的名字 Container 蘊(yùn)含著的意思,就是“倉庫、容器、集裝箱”。
NSPersistentContainer 和其它成員的關(guān)系:
進(jìn)入正式的代碼編寫的第一步,先要在使用 Core Data 框架的 Swift 文件開頭引入這個(gè)框架:
import CoreData
1
② NSPersistentContainer 的初始化
在新建的 UIKit 項(xiàng)目中,找到 AppDelegate 類,寫一個(gè)成員函數(shù)(即方法,后面直接用函數(shù)這個(gè)術(shù)語替代):
private func createPersistentContainer() { let container = NSPersistentContainer(name: "Model") }
1
2
3
這樣,NSPersistentContainer 類的建立就完成了,其中 Model 字符串就是建立的 Model.xcdatamodeld 文件,但是輸入?yún)?shù)的時(shí)候,不需要(也不應(yīng)該)輸入 .xcdatamodeld 后綴。
當(dāng)創(chuàng)建了 NSPersistentContainer 對象時(shí),僅僅完成了基礎(chǔ)的初始化,而對于一些性能開銷較大的初始化,比如本地持久化資源的加載等,都還沒有完成,必須調(diào)用 NSPersistentContainer 的成員函數(shù) loadPersistentStores 來完成它。
private func createPersistentContainer() { let container = NSPersistentContainer(name: "Model") container.loadPersistentStores { (description, error) in if let error = error { fatalError("Error: \(error)") } print("Load stores success") } }
1
2
3
4
5
6
7
8
9
從代碼設(shè)計(jì)的角度看,為什么 NSPersistentContainer 不直接在構(gòu)造函數(shù)里完成數(shù)據(jù)庫的加載?這就涉及到一個(gè)面向?qū)ο蟮拈_發(fā)原則,即構(gòu)造函數(shù)的初始化應(yīng)該是(原則上)傾向于原子級別,即簡單的、低開銷內(nèi)存操作,而對于性能開銷大的,內(nèi)存之外的存儲空間處理(比如磁盤,網(wǎng)絡(luò)),應(yīng)盡量單獨(dú)提供成員函數(shù)來完成,這樣做是為了避免在構(gòu)造函數(shù)中出錯(cuò)時(shí)錯(cuò)誤難以捕捉的問題。
③ Core Data 表格屬性信息的提供者 NSManagedObjectModel
現(xiàn)在已經(jīng)持有并成功初始化了 Core Data 的倉庫管理者 NSPersistentContainer,接下去就可以使用向這個(gè)管理者索取信息了,我們已經(jīng)在模型文件里存放了讀者和書籍這兩個(gè) Entity 了,如何獲取這兩個(gè) Entity 的信息呢?這就需要用到 NSPersistentContainer 的成員,即 managedObjectModel,該成員就是標(biāo)題所說的 NSManagedObjectModel 類型。
為了了解 NSManagedObjectModel 能提供什么,通過以下函數(shù)來提供說明:
private func parseEntities(container: NSPersistentContainer) { let entities = container.managedObjectModel.entities print("Entity count = \(entities.count)\n") for entity in entities { print("Entity: \(entity.name!)") for property in entity.properties { print("Property: \(property.name)") } print("") } }
1
2
3
4
5
6
7
8
9
10
11
為了執(zhí)行上面這個(gè)函數(shù),需要修改 createPersistentContainer,在里面調(diào)用 parseEntities:
private func createPersistentContainer() { let container = NSPersistentContainer(name: "Model") container.loadPersistentStores { (description, error) in if let error = error { fatalError("Error: \(error)") } self.parseEntities(container: container) } }
1
2
3
4
5
6
7
8
9
10
在這個(gè)函數(shù)里,通過 NSPersistentContainer 獲得了 NSManagedObjectModel 類型的成員 managedObjectModel,并通過它獲得了文件 Model.xcdatamodeld 中配置好的 Entity 信息,即圖書和讀者。由于配置了兩個(gè) Entity 信息,所以運(yùn)行正確的話,打印出來的第一行是:
Entity count = 2
1
container 的成員 managedObjectModel 有一個(gè)成員叫 entities,它是一個(gè)數(shù)組,這個(gè)數(shù)組成員的類型叫 NSEntityDescription,這個(gè)類名是專門用來處理 Entity 相關(guān)操作的,這里就沒必要多贅述。
示例代碼里,獲得了 entity 數(shù)組后,打印 entity 的數(shù)量,然后遍歷數(shù)組,逐個(gè)獲得 entity 實(shí)例,接著遍歷 entity 實(shí)例的 properties 數(shù)組,該數(shù)組成員是由類型 NSPropertyDescription 的對象組成。
關(guān)于名詞 Property,在 Core Data 的術(shù)語環(huán)境下,一個(gè) Entity 由若干信息部分組成,之前已經(jīng)提過的 Entity 和 Relationship 就是,而這些信息用術(shù)語統(tǒng)稱為 property。NSPropertyDescription 看名字就能知道,就是處理 property 用的。
Entity count = 2 Entity: Book Property: isbm Property: name Property: page Property: borrowedBy Entity: Reader Property: idCard Property: name Property: borrow
1
2
3
4
5
6
7
8
9
10
11
12
可以看到,打印出來配置的圖書有 4 個(gè) property,最后一個(gè)是 borrowedBy,明顯這是個(gè) Relationship,而前面三個(gè)都是 Attribute,這和剛剛對 property 的說明是一致的。
④ Entity 對應(yīng)的類
Core Data 是一個(gè)“對象-關(guān)系映射”持久化方案,現(xiàn)在在 Model.xcdatamodeld 已經(jīng)建立了兩個(gè) Entity,那么如果在代碼里要操作它們,是不是會(huì)有對應(yīng)的類?答案是確實(shí)如此,而且還不需要自己去定義這個(gè)類。
如果點(diǎn)擊 Model.xcdatamodeld 編輯窗口中的 Book 這個(gè) Entity,打開右側(cè)的屬性面板,屬性面板會(huì)給出允許編輯的關(guān)于這個(gè) Entity 的信息,其中 Entity 部分的 Name 就是命名的 Book,而下方還有一個(gè) Class 欄,這一欄就是跟 Entity 綁定的類信息,欄目中的 Name 就是要定義的類名,默認(rèn)它和 Entity 的名字相同,也就是說,類名也是 Book,所以改與不改,看個(gè)人思路以及團(tuán)隊(duì)的規(guī)范。
所有 Entity 對應(yīng)的類,都繼承自 NSManagedObject。為了檢驗(yàn)這一點(diǎn),可以在代碼中編寫這一行作為測試:
var book: Book! // 純測驗(yàn)代碼,無業(yè)務(wù)價(jià)值
1
如果寫下這一行編譯通過了,那說明開發(fā)環(huán)境已經(jīng)生成了 Book 這個(gè)類,不然它就不可能編譯通過。測試結(jié)果,完美編譯通過,說明不需要自己編寫,就可以直接使用這個(gè)類。
說明:
關(guān)于類名,官方教程里一般會(huì)把類名更改為 Entity 名 + MO,比如這個(gè) Entity 名為 Book,那么如果是按照官方教程的做法,可以在面板中編輯 Class 的名字為 BookMO,這里 MO 大概就是 Model Object 的簡稱。但是這里為簡潔起見,就不做任何更改了,Entity 名為 Book,那么類名也一樣為 Book。
另外,也可以自己去定義 Entity 對應(yīng)的類,這樣有個(gè)好處是可以給類添加一些額外的功能支持,這部分 Core Data 提供了編寫的規(guī)范,但是大部分時(shí)候這個(gè)做法反而會(huì)增加代碼量,不屬于常規(guī)操作。
四、數(shù)據(jù)業(yè)務(wù)的操作
① 數(shù)據(jù)操作管理類 NSManagedObjectContext
接下來,隆重介紹 NSPersistentContainer 麾下的一名工作任務(wù)最繁重的大將,成員 viewContext,接下去和實(shí)際數(shù)據(jù)打交道,處理增刪查改這四大操作,都要通過這個(gè)成員才能進(jìn)行。
viewContext 成員的類型是 NSManagedObjectContext,顧名思義,它的任務(wù)就是管理對象的上下文。從創(chuàng)建數(shù)據(jù),對修改后數(shù)據(jù)的保存、刪除數(shù)據(jù)、修改數(shù)據(jù),無一不是以它為入口。
現(xiàn)在開始,正式從“定義數(shù)據(jù)”的階段,正式進(jìn)入到“產(chǎn)生和操作數(shù)據(jù)”的階段。
② 數(shù)據(jù)的插入
“數(shù)據(jù)插入”的調(diào)用方法:NSEntityDescription.insertNewObject。
先嘗試創(chuàng)建一本圖書,用一個(gè) createBook 函數(shù)來進(jìn)行,示例代碼如下:
private func createBook(container: NSPersistentContainer, name: String, isbm: String, pageCount: Int) { let context = container.viewContext let book = NSEntityDescription.insertNewObject(forEntityName: "Book", into: context) as! Book book.name = name book.isbm = isbm book.page = Int32(pageCount) if context.hasChanges { do { try context.save() print("Insert new book(\(name)) successful.") } catch { print("\(error)") } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在這個(gè)代碼里,最值得關(guān)注的部分就是 NSEntityDescription 的靜態(tài)成員函數(shù) insertNewObject,通過這個(gè)函數(shù)來進(jìn)行所要插入數(shù)據(jù)的創(chuàng)建工作。
insertNewObject 對應(yīng)的參數(shù) forEntityName 就是要輸入的 Entity 名,這個(gè)名字當(dāng)然必須是之前創(chuàng)建好的 Entity 有的名字才行,否則就出錯(cuò),因?yàn)橐獎(jiǎng)?chuàng)建的是書,所以輸入的名字就是 Book。
而 into 參數(shù)就是處理增刪查改的大將 NSManagedObjectContext 類型。insertNewObject 返回的類型是 NSManagedObject,如前所述,這是所有 Entity 對應(yīng)類的父類,因?yàn)橐獎(jiǎng)?chuàng)建的 Entity 是 Book,我們已經(jīng)知道對應(yīng)的類名是 Book,所以可以放心大膽的把它轉(zhuǎn)換為 Book 類型。接下來就可以對 Book 實(shí)例進(jìn)行成員賦值,可以驚喜的發(fā)現(xiàn) Book 類的成員都是在 Entity 表格中編輯好的,真是方便極了。
那么問題來了,當(dāng)把 Book 編輯完成后,是不是這個(gè)數(shù)據(jù)就完成了持久化了,其實(shí)不是的。這里要提一下 Core Data 的設(shè)計(jì)理念:懶原則,Core Data 框架之下,任何原則操作都是內(nèi)存級的操作,不會(huì)自動(dòng)同步到磁盤或者其它媒介里,只有開發(fā)者主動(dòng)發(fā)出存儲命令,才會(huì)做出存儲操作,這么做自然不是因?yàn)檎娴暮軕?,而是出于性能考慮。
為了真的把數(shù)據(jù)保存起來,首先通過 context (即 NSManagedObjectContext 成員)的 hasChanges 成員詢問是否數(shù)據(jù)有改動(dòng),如果有改動(dòng),就執(zhí)行 context 的 save 函數(shù)(該函數(shù)是個(gè)會(huì)拋異常的函數(shù),所以用 do→catch 包裹起來)。至此,添加書本的操作代碼就全部寫完,接下來把它放到合適的地方運(yùn)行。
對 createPersistentContainer 稍作修改:
private func createPersistentContainer() { let container = NSPersistentContainer(name: "Model") container.loadPersistentStores { (description, error) in if let error = error { fatalError("Error: \(error)") } //self.parseEntities(container: container) self.createBook(container: container, name: "算法(第4版)", isbm: "9787115293800", pageCount: 636) } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
運(yùn)行項(xiàng)目,會(huì)看到如下打印輸出:
Insert new book(算法(第4版)) successful.
1
至此,書本的插入工作順利完成,因?yàn)檫@個(gè)示例沒有去重判定,如果程序運(yùn)行兩次,那么將會(huì)插入兩條書名都為“算法(第4版)”的 book 記錄。
③ 數(shù)據(jù)的獲取
讀取的示例代碼:
private func readBooks(container: NSPersistentContainer) { let context = container.viewContext let fetchBooks = NSFetchRequest
1
2
3
4
5
6
7
8
9
10
11
12
13
處理數(shù)據(jù)處理依然是數(shù)據(jù)操作主力 context,而處理讀取請求配置細(xì)節(jié)則是交給一個(gè)專門的類 NSFetchRequest 來完成,因?yàn)樘幚碜x取數(shù)據(jù)有各種各樣的類型,所以 Core Data 設(shè)計(jì)了一個(gè)泛型模式,只要對 NSFetchRequest 傳入對應(yīng)的類型,比如 Book,它就知道應(yīng)該傳回什么類型的對應(yīng)數(shù)組,其結(jié)果是可以通過 Entity 名為 Book 的請求直接拿到 Book 類型的數(shù)組,真是很方便。
打印結(jié)果:
Books count = 1 Book name = 算法(第4版)
1
2
④ 數(shù)據(jù)獲取的條件篩選
通過 NSFetchRequest 可以獲取所有的數(shù)據(jù),但是很多時(shí)候需要的是獲得想要的特定的數(shù)據(jù),通過條件篩選功能,可以實(shí)現(xiàn)獲取出想要的數(shù)據(jù),這時(shí)候需要用到 NSFetchRequest 的成員 predicate 來完成篩選,如下所示,要找書名叫“算法(第4版)”的書,在代碼示例里,在之前實(shí)現(xiàn)的 readBooks 函數(shù)代碼里略作修改:
private func readBooks(container: NSPersistentContainer) { let context = container.viewContext let fetchBooks = NSFetchRequest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
通過代碼:
fetchBooks.predicate = NSPredicate(format: "name = \"算法(第4版)\"")
1
從書籍中篩選出書名為 算法(第4版) 的書,因?yàn)橹耙呀?jīng)保存過這本書,所以可以正確篩選出來。篩選方案還支持大小對比,如:
fetchBooks.predicate = NSPredicate(format: "page > 100")
1
這樣將篩選出 page 數(shù)量大于 100 的書籍。
⑤ 數(shù)據(jù)的修改
當(dāng)要修改數(shù)據(jù)時(shí),比如說需要把 isbm = “9787115293800” 這本書的書名修改為“算法(第5版)” ,可以按照如下代碼示例:
let context = container.viewContext let fetchBooks = NSFetchRequest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
上面的例子里,遵循了“讀取→修改→保存”的思路,先拿到篩選的書本,然后修改書本的名字,當(dāng)名字被修改后,context 將會(huì)知道數(shù)據(jù)被修改,這時(shí)候判斷數(shù)據(jù)是否被修改(實(shí)際上不需要判斷便也知道被修改了,只是出于編碼規(guī)范加入了這個(gè)判斷),如果被修改,就保存數(shù)據(jù),通過這個(gè)方式,成功更改了書名。
⑥ 數(shù)據(jù)的刪除
數(shù)據(jù)的刪除,依然遵循“讀取→修改→保存”的思路,找到想要的思路,并且刪除它。刪除的方法是通過 context 的 delete 函數(shù)。
如下所示,刪除所有 isbm=“9787115293800” 的書籍:
let context = container.viewContext let fetchBooks = NSFetchRequest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Swift 數(shù)據(jù)管理服務(wù) DAS
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。