iOS的CoreData技術(shù)詳解

為什么寫這篇文章

最近因為新項目想用到數(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 DataSQLite做了更進一步的封裝鸳粉,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ù)模型是用來存放圖書館信息的顶猜,那么很自然的沧奴,我會想建立一個叫BookEntity

“屬性” - Attributes

當(dāng)建立一個名為BookEntity時驶兜,會看到視圖中有欄寫著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)系,而這個方式是我們來定義的倘潜。

ReaderRelationship下點擊+號鍵绷柒。然后在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下添加新的borrowByDestinationReader路召,這時候點擊Inverse一欄贮懈,會發(fā)現(xiàn)彈出了borrow,直接點上优训。

這是因為我們在定義BookRelationship之前,我們已經(jīng)定義了ReaderRelationship了各聘,所以電腦已經(jīng)知道了讀者和書籍的關(guān)系揣非,可以直接選上。而一旦選好了躲因,那么在ReaderRelationship中早敬,我們會發(fā)現(xiàn)Inverse一欄會自動補齊為borrowBy忌傻。因為電腦這時候已經(jīng)完全理解了雙方的關(guān)系,自動做了補齊搞监。

“一對一”和“一對多” - to one和to many

我們建立ReaderBook之間的聯(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è)計角钩。

p4

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)提過的EntityRelationship就是了栗恩。而這些信息用術(shù)語統(tǒng)稱為propertyNSPropertyDescription看名字就能知道洪燥,就是處理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)建的EntityBook千康,我們已經(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í)行contextsave函數(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ù)的刪除依然遵循 讀取→修改→保存 的思路轰豆,找到我們想要的思路,并且刪除它握恳。刪除的方法是通過contextdelete函數(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ā)的進階部分吼野,我在這里簡單列舉一下:

  1. Relationship部分的開發(fā),事實上通過之前的知識可以獨立完成两波。
  2. 回滾操作瞳步,相關(guān)類:UndoManager
  3. EntityFetched Property屬性腰奋。
  4. 多個context一起操作數(shù)據(jù)的沖突問題单起。
  5. 持久化層的管理,包括遷移文件地址劣坊,設(shè)置多個存儲源等馏臭。

以上諸個主題都可以自己進一步探索,不在這篇文章的講解范圍。不過后續(xù)不排除會單獨出文探索括儒。

結(jié)語

Core Data在圈內(nèi)是比較出了名的“不好用”的框架绕沈,主要是因為其抽象的功能和機制較為不容易理解。本文已經(jīng)以最大限度的努力試圖從設(shè)計的角度去闡述該框架帮寻,希望對你有所幫助乍狐。



引用:https://developer.apple.com/documentation/coredata

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市固逗,隨后出現(xiàn)的幾起案子浅蚪,更是在濱河造成了極大的恐慌,老刑警劉巖烫罩,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惜傲,死亡現(xiàn)場離奇詭異,居然都是意外死亡贝攒,警方通過查閱死者的電腦和手機盗誊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隘弊,“玉大人哈踱,你說我怎么就攤上這事±嫖酰” “怎么了开镣?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長咽扇。 經(jīng)常有香客問我邪财,道長,這世上最難降的妖魔是什么质欲? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任卧蜓,我火速辦了婚禮,結(jié)果婚禮上把敞,老公的妹妹穿的比我還像新娘。我一直安慰自己榨惠,他們只是感情好奋早,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著赠橙,像睡著了一般耽装。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上期揪,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天掉奄,我揣著相機與錄音,去河邊找鬼。 笑死姓建,一個胖子當(dāng)著我的面吹牛诞仓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播速兔,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼墅拭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涣狗?” 一聲冷哼從身側(cè)響起谍婉,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎镀钓,沒想到半個月后穗熬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡丁溅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年唤蔗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片唧瘾。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡措译,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饰序,到底是詐尸還是另有隱情领虹,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布求豫,位于F島的核電站塌衰,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蝠嘉。R本人自食惡果不足惜最疆,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蚤告。 院中可真熱鬧努酸,春花似錦、人聲如沸杜恰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽心褐。三九已至舔涎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逗爹,已是汗流浹背亡嫌。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挟冠。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓于购,卻偏偏與公主長得像,于是被迫代替她去往敵國和親圃郊。 傳聞我的和親對象是個殘疾皇子价涝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容