為什么寫這篇文章
最近因為新項目想用到數(shù)據(jù)持久化十拣,本來這是很簡單的事情,復(fù)雜數(shù)據(jù)一般直接SQLite
就可以解決了刨疼。
但是一直以來使用SQLite
確實存在要自己設(shè)計數(shù)據(jù)庫专缠,處理邏輯編碼,還有調(diào)試方面的種種繁瑣問題捂龄。所以考慮使用iOS的Core Data方案释涛。
上網(wǎng)查了一堆資料后,發(fā)現(xiàn)很多代碼都已經(jīng)是陳舊的了倦沧。甚至蘋果官方文檔提供的代碼樣例都未必是最新的Swift
版本唇撬。于是萌生了自己寫一篇文章來整理一遍思路的想法。盡可能讓新人快速的上手展融,不但要知道其然窖认,還要知道其設(shè)計的所以然,這樣用起來才更得心應(yīng)手告希。
什么是Core Data
我們寫app肯定要用到數(shù)據(jù)持久化扑浸,說白了,就是把數(shù)據(jù)保存起來暂雹,app不刪除的話可以繼續(xù)讀寫首装。
iOS提供數(shù)據(jù)持久化的方案有很多,各自有其特定用途杭跪。
比如很多人熟知的UserDefaults
仙逻,大部分時候是用來保存簡單的應(yīng)用配置信息驰吓;而NSKeyedArchiver
可以把代碼中的對象保存為文件,方便后來重新讀取系奉。
另外還有個常用的保存方式就是自己創(chuàng)建文件檬贰,直接在磁盤文件中進行讀寫。
而對于稍微復(fù)雜的業(yè)務(wù)數(shù)據(jù)缺亮,比如收藏夾翁涤,用戶填寫的多項表格等,SQLite
就是更合適的方案了萌踱。關(guān)于數(shù)據(jù)庫的知識葵礼,我這里就不贅述了,稍微有點技術(shù)基礎(chǔ)的童鞋都懂并鸵。
Core Data
比SQLite
做了更進一步的封裝鸳粉,SQLite
提供了數(shù)據(jù)的存儲模型,并提供了一系列API园担,你可以通過API讀寫數(shù)據(jù)庫届谈,去處理想要處理的數(shù)據(jù)。但是SQLite
存儲的數(shù)據(jù)和你編寫代碼中的數(shù)據(jù)(比如一個類的對象)并沒有內(nèi)置的聯(lián)系弯汰,必須你自己編寫代碼去一一對應(yīng)艰山。
而Core Data
卻可以解決一個數(shù)據(jù)在持久化層和代碼層的一一對應(yīng)關(guān)系。也就是說咏闪,你處理一個對象的數(shù)據(jù)后曙搬,通過保存接口,它可以自動同步到持久化層里汤踏,而不需要你去實現(xiàn)額外的代碼织鲸。
這種 對象→持久化 方案叫 對象→關(guān)系映射(英文簡稱ORM
)。
除了這個最重要的特性溪胶,Core Data
還提供了很多有用的特性,比如回滾機制稳诚,數(shù)據(jù)校驗等哗脖。
數(shù)據(jù)模型文件 - Data Model
當(dāng)我們用Core Data
時,我們需要一個用來存放數(shù)據(jù)模型的地方扳还,數(shù)據(jù)模型文件就是我們要創(chuàng)建的文件類型才避。它的后綴是.xcdatamodeld
。只要在項目中選 新建文件→Data Model 即可創(chuàng)建氨距。
默認系統(tǒng)提供的命名為 Model.xcdatamodeld
桑逝。下面我依然以 Model.xcdatamodeld
作為舉例的文件名。
這個文件就相當(dāng)于數(shù)據(jù)庫中的“庫”俏让。通過編輯這個文件楞遏,就可以去添加定義自己想要處理的數(shù)據(jù)類型茬暇。
數(shù)據(jù)模型中的“表格” - Entity
當(dāng)在xcode中點擊Model.xcdatamodeld
時,會看到蘋果提供的編輯視圖寡喝,其中有個醒目的按鈕Add Entity
糙俗。
什么是Entity
呢?中文翻譯叫“實體”预鬓,但是我這里就不打算用各種翻譯名詞來提高理解難度了巧骚。
如果把數(shù)據(jù)模型文件比作數(shù)據(jù)庫中的“庫”,那么Entity
就相當(dāng)于庫里的“表格”格二。這么理解就簡單了劈彪。Entity
就是讓你定義數(shù)據(jù)表格類型的名詞。
假設(shè)我這個數(shù)據(jù)模型是用來存放圖書館信息的顶猜,那么很自然的沧奴,我會想建立一個叫Book
的Entity
。
“屬性” - Attributes
當(dāng)建立一個名為Book
的Entity
時驶兜,會看到視圖中有欄寫著Attributes
扼仲,我們知道,當(dāng)我們定義一本書時抄淑,自然要定義書名屠凶,書的編碼等信息。這部分信息叫Attributes
肆资,即書的屬性矗愧。
Book的Entity
:
屬性名 | 類型 |
---|---|
name | String |
isbm | String |
page | Integer32 |
其中,類型部分大部分是大家熟知的元數(shù)據(jù)類型郑原,可以自行查閱唉韭。
同理,也可以再添加一個讀者:Reader的Entity
描述犯犁。
Reader的Entity
:
屬性名 | 類型 |
---|---|
name | String |
idCard | String |
“關(guān)系” - Relationship
在我們使用Entity
編輯時属愤,除了看到了Attributes
一欄,還看到下面有Relationships
一欄酸役,這欄是做什么的住诸?
回到例子中來,當(dāng)定義圖書館信息時涣澡,剛書籍和讀者的信息贱呐,但這兩個信息彼此是孤立的,而事實上他們存在著聯(lián)系入桂。
比如一本書奄薇,它被某個讀者借走了,這樣的數(shù)據(jù)該怎么存儲抗愁?
直觀的做法是再定義一張表格來處理這類關(guān)系馁蒂。但是Core Data
提供了更有效的辦法 - Relationship
呵晚。
從Relationship
的思路來思考,當(dāng)一本書A被某個讀者B借走远搪,我們可以理解為這本書A當(dāng)前的“借閱者”是該讀者B劣纲,而讀者B的“持有書”是A。
從以上描述可以看出谁鳍,Relationship
所描述的關(guān)系是雙向的癞季,即A和B互相以某種方式形成了聯(lián)系,而這個方式是我們來定義的倘潜。
在Reader
的Relationship
下點擊+
號鍵绷柒。然后在Relationship
欄的名字上填borrow
,表示讀者和書的關(guān)系是“借閱”涮因,在Destination
欄選擇Book
废睦,這樣,讀者和書籍的關(guān)系就確立了养泡。
對于第三欄嗜湃,Inverse
,卻沒有東西可以填澜掩,這是為什么购披?
因為我們現(xiàn)在定義了讀者和書的關(guān)系,卻沒有定義書和讀者的關(guān)系肩榕。記住刚陡,關(guān)系是雙向的。
就好比你定義了A是B的父親株汉,那也要同時去定義B是A的兒子一個道理筐乳。計算機不會幫我們打理另一邊的聯(lián)系。
理解了這點乔妈,我們開始選擇Book
的一欄蝙云,在Relationship
下添加新的borrowBy
,Destination
是Reader
路召,這時候點擊Inverse
一欄贮懈,會發(fā)現(xiàn)彈出了borrow
,直接點上优训。
這是因為我們在定義Book
的Relationship
之前,我們已經(jīng)定義了Reader
的Relationship
了各聘,所以電腦已經(jīng)知道了讀者和書籍的關(guān)系揣非,可以直接選上。而一旦選好了躲因,那么在Reader
的Relationship
中早敬,我們會發(fā)現(xiàn)Inverse
一欄會自動補齊為borrowBy
忌傻。因為電腦這時候已經(jīng)完全理解了雙方的關(guān)系,自動做了補齊搞监。
“一對一”和“一對多” - to one和to many
我們建立Reader
和Book
之間的聯(lián)系的時候水孩,發(fā)現(xiàn)他們的聯(lián)系邏輯之間還漏了一個環(huán)節(jié)。
假設(shè)一本書被一個讀者借走了琐驴,它就不能被另一個讀者借走俘种,而當(dāng)一個讀者借書時,卻可以借很多本書绝淡。
也就是說宙刘,一本書只能對應(yīng)一個讀者,而一個讀者卻可以對應(yīng)多本書牢酵。
這就是 一對一→to one
和 一對多→to many
悬包。
Core Data
允許我們配置這種聯(lián)系,具體做法就是在RelationShip
欄點擊對應(yīng)的關(guān)系欄馍乙,它將會出現(xiàn)在右側(cè)的欄目中茵烈。(欄目如果沒出現(xiàn)可以在xcode
右上角的按鈕調(diào)出,如果點擊后欄目沒出現(xiàn)Relationship
配置項菇爪,可以多點擊幾下蹦魔,這是xcode
的小bug)。
在Relationship
的配置項里铁追,有一項項名為Type
季蚂,點擊后有兩個選項,一個是To One
(默認值)琅束,另一個就是To Many
了扭屁。
Core Data框架的主倉庫 - NSPersistentContainer
當(dāng)我們配置完Core Data
的數(shù)據(jù)類型信息后,我們并沒有產(chǎn)生任何數(shù)據(jù)涩禀,就好比圖書館已經(jīng)制定了圖書的規(guī)范 - 一本書應(yīng)該有名字料滥、isbm、頁數(shù)等信息艾船,規(guī)范雖然制定了葵腹,卻沒有真的引進書進來。
那么怎么才能產(chǎn)生和處理數(shù)據(jù)呢屿岂,這就需要通過代碼真刀真槍的和Core Data
打交道了践宴。
由于Core Data
的功能較為強大,必須分成多個類來處理各種邏輯爷怀,一次性學(xué)習(xí)多個類是不容易的阻肩,還容易混淆,所以后續(xù)我會分別一一列出运授。
要和這些各司其職的類打交道烤惊,我們不得不提第一個要介紹的類乔煞,叫NSPersistentContainer
,因為它就是存放這多個類成員的“倉庫類”柒室。
這個NSPersistentContainer
渡贾,就是我們通過代碼和Core Data
打交道的第一個目標。它存放著幾種讓我們和Core Data
進行業(yè)務(wù)處理的工具雄右,當(dāng)我們拿到這些工具之后空骚,就可以自由的訪問數(shù)據(jù)了。所以它的名字 - Container
蘊含著的意思不脯,就是 倉庫府怯、容器、集裝箱防楷。
進入正式的代碼編寫的第一步牺丙,我們先要在使用Core Data
框架的swift
文件開頭引入這個框架:
import CoreData
早期,在iOS 10之前复局,還沒有
NSPersistentContainer
這個類冲簿,所以Core Data
提供的幾種各司其職的工具,我們都要寫代碼一一獲得亿昏,寫出來的代碼較為繁瑣峦剔,所以NSPersistentContainer
并不是一開始就有的,而是蘋果框架設(shè)計者逐步優(yōu)化出來的較優(yōu)設(shè)計角钩。
NSPersistentContainer的初始化
在新建的UIKIT
項目中吝沫,找到我們的AppDelegate
類,寫一個成員函數(shù)(即方法递礼,后面我直接用函數(shù)這個術(shù)語替代):
private func createPersistentContainer() {
let container = NSPersistentContainer(name: "Model")
}
這樣惨险,NSPersistentContainer
類的建立就完成了,其中"Model"字符串就是我們建立的Model.xcdatamodeld
文件脊髓。但是輸入?yún)?shù)的時候辫愉,我們不需要(也不應(yīng)該)輸入.xcdatamodeld
后綴。
當(dāng)我們創(chuàng)建了NSPersistentContainer
對象時将硝,僅僅完成了基礎(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")
}
}
從代碼設(shè)計的角度看律罢,為什么
NSPersistentContainer
不直接在構(gòu)造函數(shù)里完成數(shù)據(jù)庫的加載诽嘉?這就涉及到一個面向?qū)ο蟮拈_發(fā)原則,即構(gòu)造函數(shù)的初始化應(yīng)該是(原則上)傾向于原子級別,即簡單的虫腋、低開銷內(nèi)存操作,而對于性能開銷大的稀余,內(nèi)存之外的存儲空間處理(比如磁盤悦冀,網(wǎng)絡(luò)),應(yīng)盡量單獨提供成員函數(shù)來完成睛琳。這樣做是為了避免在構(gòu)造函數(shù)中出錯時錯誤難以捕捉的問題盒蟆。
表格屬性信息的提供者 - NSManagedObjectModel
現(xiàn)在我們已經(jīng)持有并成功初始化了Core Data
的倉庫管理者NSPersistentContainer
了,接下去我們可以使用向這個管理者索取信息了师骗,我們已經(jīng)在模型文件里存放了讀者和書籍這兩個Entity
了历等,如何獲取這兩個Entity的信息?
這就需要用到NSPersistentContainer
的成員辟癌,即managedObjectModel
寒屯,該成員就是標題所說的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("")
}
}
為了執(zhí)行上面這個函數(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)
}
}
在這個函數(shù)里厂置,我們通過NSPersistentContainer
獲得了NSManagedObjectModel
類型的成員managedObjectModel
菩掏,并通過它獲得了文件Model.xcdatamodeld
中我們配置好的Entity
信息,即圖書和讀者昵济。
由于我們配置了兩個Entity
信息智绸,所以運行正確的話,打印出來的第一行應(yīng)該是Entity count = 2
访忿。
container
的成員managedObjectModel
有一個成員叫entities
瞧栗,它是一個數(shù)組,這個數(shù)組成員的類型叫NSEntityDescription
醉顽,這個類名一看就知道是專門用來處理Entity
相關(guān)操作的沼溜,這里就沒必要多贅述了。
示例代碼里游添,獲得了entity
數(shù)組后系草,打印entity
的數(shù)量,然后遍歷數(shù)組唆涝,逐個獲得entity
實例找都,接著遍歷entity
實例的properties
數(shù)組,該數(shù)組成員是由類型NSPropertyDescription
的對象組成廊酣。
關(guān)于名詞Property
能耻,不得不單獨說明下,學(xué)習(xí)一門技術(shù)最煩人的事情之一就是理解各種名詞,畢竟不同技術(shù)之間名詞往往不一定統(tǒng)一晓猛,所以要單獨理解一下饿幅。
在Core Data
的術(shù)語環(huán)境下,一個Entity
由若干信息部分組成戒职,之前已經(jīng)提過的Entity
和Relationship
就是了栗恩。而這些信息用術(shù)語統(tǒng)稱為property
。NSPropertyDescription
看名字就能知道洪燥,就是處理property
用的磕秤。
只要將這一些知識點梳理清楚了,接下去打印的內(nèi)容就不難懂了:
Entity count = 2
Entity: Book
Property: isbm
Property: name
Property: page
Property: borrowedBy
Entity: Reader
Property: idCard
Property: name
Property: borrow
我們看到捧韵,打印出來我們配置的圖書有4個property市咆,最后一個是borrowedBy,明顯這是個Relationship
再来,而前面三個都是Attribute
蒙兰,這和我剛剛對property的說明是一致的。
Entity對應(yīng)的類
開篇我們就講過其弊,Core Data
是一個 對象-關(guān)系映射 持久化方案癞己,現(xiàn)在我們在Model.xcdatamodeld
已經(jīng)建立了兩個Entity
,那么如果在代碼里要操作他們梭伐,是不是會有對應(yīng)的類痹雅?
答案是確實如此,而且你還不需要自己去定義這個類糊识。
如果你點擊Model.xcdatamodeld
編輯窗口中的Book這個Entity
绩社,打開右側(cè)的屬性面板,屬性面板會給出允許你編輯的關(guān)于這個Entity
的信息赂苗,其中Entity
部分的Name
就是我們起的名字Book
愉耙,而下方還有一個Class
欄,這一欄就是跟Entity
綁定的類信息拌滋,欄目中的Name
就是我們要定義的類名朴沿,默認它和Entity
的名字相同,也就是說败砂,類名也是Book
赌渣。所以改與不改,看個人思路以及團隊的規(guī)范昌犹。
所有Entity
對應(yīng)的類坚芜,都繼承自NSManagedObject
。
為了檢驗這一點斜姥,我們可以在代碼中編寫這一行作為測試:
var book: Book! // 純測驗代碼鸿竖,無業(yè)務(wù)價值
如果寫下這一行編譯通過了沧竟,那說明開發(fā)環(huán)境已經(jīng)給我們生成了Book
這個類,不然它就不可能編譯通過缚忧。
測試結(jié)果悟泵,完美編譯通過。說明不需要我們自己編寫搔谴,就可以直接使用這個類了魁袜。
關(guān)于類名芜果,官方教程里一般會把類名更改為
Entity名 + MO
脆烟,比如我們這個Entity
名為Book
敏簿,那么如果是按照官方教程的做法咙边,可以在面板中編輯Class
的名字為BookMO
搞挣,這里MO
大概就是Model Object
的簡稱吧财边。但是我這里為簡潔起見,就不做任何更改了,
Entity
名為Book
椎侠,那么類名也一樣為Book
我纪。
另外泼舱,你也可以自己去定義
Entity
對應(yīng)的類,這樣有個好處是可以給類添加一些額外的功能支持蹲盘,這部分Core Data
提供了編寫的規(guī)范股毫,但是大部分時候這個做法反而會增加代碼量,不屬于常規(guī)操作召衔。
數(shù)據(jù)業(yè)務(wù)的操作員 - NSManagedObjectContext
接下來我們要隆重介紹NSPersistentContainer
麾下的一名工作任務(wù)最繁重的大將铃诬,成員viewContext
,接下去我們和實際數(shù)據(jù)打交道苍凛,處理增刪查改這四大操作趣席,都要通過這個成員才能進行。
viewContext
成員的類型是NSManagedObjectContext
醇蝴。
NSManagedObjectContext
宣肚,顧名思義,它的任務(wù)就是管理對象的上下文悠栓。從創(chuàng)建數(shù)據(jù)霉涨,對修改后數(shù)據(jù)的保存按价,刪除數(shù)據(jù),修改嵌纲,五一不是以它為入口俘枫。
從介紹這個成員開始,我們就正式從 定義數(shù)據(jù) 的階段逮走,正式進入到 產(chǎn)生和操作數(shù)據(jù) 的階段鸠蚪。
數(shù)據(jù)的插入 - NSEntityDescription.insertNewObject
梳理完前面的知識,就可以正式踏入數(shù)據(jù)創(chuàng)建的學(xué)習(xí)了师溅。
這里茅信,我們先嘗試創(chuàng)建一本圖書,用一個createBook
函數(shù)來進行墓臭。示例代碼如下:
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)")
}
}
}
在這個代碼里蘸鲸,最值得關(guān)注的部分就是NSEntityDescription
的靜態(tài)成員函數(shù)insertNewObject
了,我們就是通過這個函數(shù)來進行所要插入數(shù)據(jù)的創(chuàng)建工作窿锉。
insertNewObject
對應(yīng)的參數(shù)forEntityName
就是我們要輸入的Entity
名酌摇,這個名字當(dāng)然必須是我們之前創(chuàng)建好的Entity
有的名字才行,否則就出錯了嗡载。因為我們要創(chuàng)建的是書窑多,所以輸入的名字就是Book
。
而into
參數(shù)就是我們的處理增刪查改的大將NSManagedObjectContext
類型洼滚。
insertNewObject
返回的類型是NSManagedObject
埂息,如前所述,這是所有Entity
對應(yīng)類的父類遥巴。因為我們要創(chuàng)建的Entity
是Book
千康,我們已經(jīng)知道對應(yīng)的類名是Book
了,所以我們可以放心大膽的把它轉(zhuǎn)換為Book
類型铲掐。
接下來我們就可以對Book
實例進行成員賦值拾弃,我們可以驚喜的發(fā)現(xiàn)Book
類的成員都是我們在Entity
表格中編輯好的,真是方便極了迹炼。
那么問題來了砸彬,當(dāng)我們把Book
編輯完成后,是不是這個數(shù)據(jù)就完成了持久化了斯入,其實不是的砂碉。
這里要提一下Core Data
的設(shè)計理念:懶原則。Core Data
框架之下刻两,任何原則操作都是內(nèi)存級的操作增蹭,不會自動同步到磁盤或者其他媒介里,只有開發(fā)者主動發(fā)出存儲命令磅摹,才會做出存儲操作滋迈。這么做自然不是因為真的很懶霎奢,而是出于性能考慮。
為了真的把數(shù)據(jù)保存起來饼灿,首先我們通過context
(即NSManagedObjectContext
成員)的hasChanges
成員詢問是否數(shù)據(jù)有改動幕侠,如果有改動,就執(zhí)行context
的save
函數(shù)碍彭。(該函數(shù)是個會拋異常的函數(shù)晤硕,所以用do→catch
包裹起來)。
至此庇忌,添加書本的操作代碼就寫完了舞箍。接下來我們把它放到合適的地方運行。
我們對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)
}
}
運行項目皆疹,會看到如下打印輸出:
Insert new book(算法(第4版)) successful.
至此疏橄,書本的插入工作順利完成!
因為這個示例沒有去重判定略就,如果程序運行兩次捎迫,那么將會插入兩條書名都為"算法(第4版)"的
book
記錄。
數(shù)據(jù)的獲取
有了前面基礎(chǔ)知識的鋪墊表牢,接下去的例子只要 記函數(shù) 就成了立砸,讀取的示例代碼:
private func readBooks(container: NSPersistentContainer) {
let context = container.viewContext
let fetchBooks = NSFetchRequest<Book>(entityName: "Book")
do {
let books = try context.fetch(fetchBooks)
print("Books count = \(books.count)")
for book in books {
print("Book name = \(book.name!)")
}
} catch {
}
}
處理數(shù)據(jù)處理依然是我們的數(shù)據(jù)操作主力context
,而處理讀取請求配置細節(jié)則是交給一個專門的類初茶,NSFetchRequest
來完成,因為我們處理讀取數(shù)據(jù)有各種各樣的類型浊闪,所以Core Data
設(shè)計了一個泛型模式恼布,你只要對NSFetchRequest
傳入對應(yīng)的類型,比如Book
搁宾,它就知道應(yīng)該傳回什么類型的對應(yīng)數(shù)組折汞,其結(jié)果是,我們可以通過Entity
名為Book
的請求直接拿到Book
類型的數(shù)組盖腿,真是很方便爽待。
打印結(jié)果:
Books count = 1
Book name = 算法(第4版)
數(shù)據(jù)獲取的條件篩選 - NSPredicate
通過NSFetchRequest
我們可以獲取所有的數(shù)據(jù),但是我們很多時候需要的是獲得我們想要的特定的數(shù)據(jù)翩腐,通過條件篩選功能鸟款,可以實現(xiàn)獲取出我們想要的數(shù)據(jù),這時候需要用到NSFetchRequest
的成員predicate
來完成篩選茂卦,如下所示何什,我們要找書名叫 算法(第4版) 的書。
在新的代碼示例里等龙,我們在之前實現(xiàn)的readBooks
函數(shù)代碼里略作修改:
private func readBooks(container: NSPersistentContainer) {
let context = container.viewContext
let fetchBooks = NSFetchRequest<Book>(entityName: "Book")
fetchBooks.predicate = NSPredicate(format: "name = \"算法(第4版)\"")
do {
let books = try context.fetch(fetchBooks)
print("Books count = \(books.count)")
for book in books {
print("Book name = \(book.name!)")
}
} catch {
print("\(error)")
}
}
通過代碼:
fetchBooks.predicate = NSPredicate(format: "name = \"算法(第4版)\"")
我們從書籍中篩選出書名為 算法(第4版) 的書处渣,因為我們之前已經(jīng)保存過這本書伶贰,所以可以正確篩選出來。
篩選方案還支持大小對比罐栈,如
fetchBooks.predicate = NSPredicate(format: "page > 100")
這樣將篩選出page數(shù)量大于100的書籍黍衙。
數(shù)據(jù)的修改
當(dāng)我們要修改數(shù)據(jù)時,比如說我們要把 isbm = "9787115293800"
這本書書名修改為 算法(第5版) 荠诬,可以按照如下代碼示例:
let context = container.viewContext
let fetchBooks = NSFetchRequest<Book>(entityName: "Book")
fetchBooks.predicate = NSPredicate(format: "isbm = \"9787115293800\"")
do {
let books = try context.fetch(fetchBooks)
if !books.isEmpty {
books[0].name = "算法(第5版)"
if context.hasChanges {
try context.save()
print("Update success.")
}
}
} catch {
print("\(error)")
}
在這個例子里琅翻,我們遵循了 讀取→修改→保存 的思路,先拿到篩選的書本浅妆,然后修改書本的名字望迎,當(dāng)名字被修改后,context
將會知道數(shù)據(jù)被修改了凌外,這時候判斷數(shù)據(jù)是否被修改(實際上不需要判斷我們也知道被修改了辩尊,只是出于編碼規(guī)范加入了這個判斷),如果被修改康辑,就保存數(shù)據(jù)摄欲,通過這個方式,成功更改了書名疮薇。
數(shù)據(jù)的刪除
數(shù)據(jù)的刪除依然遵循 讀取→修改→保存 的思路轰豆,找到我們想要的思路,并且刪除它握恳。刪除的方法是通過context
的delete
函數(shù)梭纹。
以下例子中,我們刪除了所有 isbm="9787115293800"
的書籍:
let context = container.viewContext
let fetchBooks = NSFetchRequest<Book>(entityName: "Book")
fetchBooks.predicate = NSPredicate(format: "isbm = \"9787115293800\"")
do {
let books = try context.fetch(fetchBooks)
for book in books {
context.delete(books[0])
}
if context.hasChanges {
try context.save()
}
} catch {
print("\(error)")
}
擴展和進階主題的介紹
如果跟我一步步走到這里励七,那么關(guān)于Core Data
的基礎(chǔ)知識可以說已經(jīng)掌握的差不多了智袭。
當(dāng)然了,這部分基礎(chǔ)對于日常開發(fā)已經(jīng)基本夠用了掠抬。
關(guān)于Core Data
開發(fā)的進階部分吼野,我在這里簡單列舉一下:
-
Relationship
部分的開發(fā),事實上通過之前的知識可以獨立完成两波。 - 回滾操作瞳步,相關(guān)類:
UndoManager
。 -
Entity
的Fetched Property
屬性腰奋。 - 多個
context
一起操作數(shù)據(jù)的沖突問題单起。 - 持久化層的管理,包括遷移文件地址劣坊,設(shè)置多個存儲源等馏臭。
以上諸個主題都可以自己進一步探索,不在這篇文章的講解范圍。不過后續(xù)不排除會單獨出文探索括儒。
結(jié)語
Core Data
在圈內(nèi)是比較出了名的“不好用”的框架绕沈,主要是因為其抽象的功能和機制較為不容易理解。本文已經(jīng)以最大限度的努力試圖從設(shè)計的角度去闡述該框架帮寻,希望對你有所幫助乍狐。
引用:https://developer.apple.com/documentation/coredata