swift4 0 ebook在地區(qū)獨(dú)家翻譯和銷售_第1頁(yè)
swift4 0 ebook在地區(qū)獨(dú)家翻譯和銷售_第2頁(yè)
swift4 0 ebook在地區(qū)獨(dú)家翻譯和銷售_第3頁(yè)
swift4 0 ebook在地區(qū)獨(dú)家翻譯和銷售_第4頁(yè)
swift4 0 ebook在地區(qū)獨(dú)家翻譯和銷售_第5頁(yè)
已閱讀5頁(yè),還剩272頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

本書(shū)使?CoreData的?式關(guān)于Swift的?些說(shuō)明CoreData架構(gòu)數(shù)據(jù)建模設(shè)置CoreData棧顯?數(shù)據(jù)操作數(shù)據(jù)總結(jié)iOS10/macOS10.12之前的版本需要注意的地?添加實(shí)體創(chuàng)建關(guān)系其他類型的關(guān)系建?關(guān)系關(guān)系和刪除適配??界?總結(jié)標(biāo)準(zhǔn)數(shù)據(jù)類型原始屬性和臨時(shí)屬性?定義數(shù)據(jù)類型默認(rèn)值和可選值總結(jié) 獲取請(qǐng)求關(guān)系內(nèi)存考量總結(jié)變更追蹤保存更改批量更新總結(jié)CoreData棧的性能特質(zhì)避免獲取請(qǐng)求優(yōu)化獲取請(qǐng)求插?和修改對(duì)象字符串和?本獨(dú)家秘訣的可調(diào)參數(shù)總結(jié)第3部分組織和設(shè)置同步架構(gòu)上下?屬主響應(yīng)本地更改響應(yīng)更改139并發(fā)的規(guī)則合并更改155總結(jié)保存及合并策略查詢世代刪除對(duì)象唯?性約束總結(jié) 簡(jiǎn)單謂詞?代碼來(lái)創(chuàng)建謂詞格式字符串合并多個(gè)謂詞遍歷關(guān)系匹配對(duì)象和對(duì)象ID匹配字符串總結(jié)Unicode的復(fù)雜性搜索排序總結(jié)?定義映射模型數(shù)據(jù)遷移和??界?測(cè)試數(shù)據(jù)遷移總結(jié)SQL調(diào)試輸出線程保護(hù)總結(jié)?個(gè)嵌?式數(shù)據(jù)庫(kù)數(shù)據(jù)表,列,以及?數(shù)據(jù)庫(kù)系統(tǒng)的結(jié)構(gòu)數(shù)據(jù)庫(kù)語(yǔ)?SQL關(guān)系事務(wù)索引?志總結(jié)1CoreData是AppleiOS,macOS,watchOStvOS設(shè)計(jì)的對(duì)象圖管理(objectgraphmanagement和數(shù)據(jù)持久化框架。如果你的app需要結(jié)構(gòu)化的數(shù)據(jù),CoreData是?個(gè)顯Data是?個(gè)成熟,經(jīng)過(guò)實(shí)踐檢驗(yàn)的代碼庫(kù)。然?CoreData剛開(kāi)始會(huì)讓?有些困惑:它?常靈活,但是API的最佳實(shí)踐卻并?顯?易?。題?章中將對(duì)其進(jìn)?深?研究,在另外?章我們還會(huì)實(shí)際演??個(gè)同步?案的例?。除此之外,CoreData也經(jīng)常被吐槽性能糟糕。如果你像使?關(guān)系型數(shù)據(jù)庫(kù)那樣來(lái)使?Core如果把CoreData當(dāng)成?個(gè)對(duì)象圖管理系統(tǒng)來(lái)正確使?的時(shí)候,得益于內(nèi)建的緩存和對(duì)象管理機(jī)制,它在很多??實(shí)際上反?更快。此外,抽象級(jí)別更?的API可以讓你專注于優(yōu)化app?關(guān)鍵部分的性能,?不是從頭開(kāi)始來(lái)實(shí)現(xiàn)如何持久化。本書(shū)中,我們會(huì)介紹保持CoreData?性能的最佳實(shí)踐。在專?講性能以及性能分析的章節(jié)中探討如何解決CoreData的性能本書(shū)展?了如何在實(shí)際例?中使?CoreData,?不僅僅是簡(jiǎn)單地對(duì)API?冊(cè)進(jìn)??些擴(kuò)展。分往往是最?的。 例程序來(lái)演?CoreData在較?的項(xiàng)?中?臨的和相應(yīng)的解決?案。 我們還想告訴你的是,即便在簡(jiǎn)單的應(yīng)?場(chǎng)景中,CoreData也會(huì)?常有?。式數(shù)據(jù)時(shí)會(huì)發(fā)?什么,我們也會(huì)對(duì)插?或者操作數(shù)據(jù)時(shí)發(fā)?的情況進(jìn)?研究。這部分所覆第四部分涉及?些?級(jí)的,?如:?級(jí)的謂詞(predicate),搜索和?本排序,如何在不同的數(shù)據(jù)模型版本之間遷移數(shù)據(jù),以及分析CoreData棧的性能時(shí)所需要的?具和技術(shù)等。這部分中有?章是從CoreData視?介紹有關(guān)關(guān)系數(shù)據(jù)庫(kù)和SQL查詢語(yǔ)?的基本知識(shí)的。如果你不熟悉這些內(nèi)容,這些章節(jié)能對(duì)你有所幫助,特別是可以讓你理解CoreData潛在的性能問(wèn)題,貫穿本書(shū),我們所有的?例都使?Swift編寫(xiě)。我們擁抱Swift的語(yǔ)?特性—?如泛型、協(xié)議以及擴(kuò)展—它們能讓我們更優(yōu)雅、簡(jiǎn)單、安全地使?CoreData的API。Swift表?的最佳實(shí)踐和設(shè)計(jì)模式同樣也適?于Objective-C的代碼。實(shí)現(xiàn)上由于語(yǔ)?上的Swift提供了Optional數(shù)據(jù)類型,這迫使我們顯式地思考和處理沒(méi)有值的情況。我們?常喜歡因此我們盡量避免使?Swift的操作符來(lái)強(qiáng)制解包(包括?它來(lái)定義隱式解包類型的?法),唯?的例外是那些必須設(shè)置但??法在初始化時(shí)設(shè)置的屬性。?如InterfaceBuilder的outlets或必要的(delegate)屬性等。在這些情況下,使?隱式解包的可選值符合“盡早崩潰原則:我們會(huì)?刻知曉這些必須要設(shè)置??沒(méi)有正確設(shè)置的屬性。CoreData中好些? 對(duì)于這些類型的錯(cuò)誤,我們使?Swift的try!或fatalError()來(lái)盡可能早地讓?xiě)?yīng)?程序同樣的思想可以適?于as!操作符的強(qiáng)制類型轉(zhuǎn)換如果我們知道?個(gè)對(duì)象必須是某種類型,轉(zhuǎn)換失敗的唯?原因會(huì)是邏輯錯(cuò)誤,這種時(shí)候我們實(shí)際上是希望應(yīng)?程序的。很多時(shí)候我們?Swift的guard關(guān)鍵字來(lái)更好地表達(dá)哪些地?出錯(cuò)了。舉個(gè)例?,如果我們知道托管對(duì)象的managedObjectContext屬性?定是?nil的,那么我們就可以使??個(gè)guardlet語(yǔ)句,并在else分??顯式地調(diào)?fatalError。這?直接強(qiáng)制解包更能清楚地表對(duì)于可恢復(fù)的?邏輯性錯(cuò)誤,我們使?Swift的錯(cuò)誤傳遞?法:拋出(throw)2據(jù)庫(kù)會(huì)是怎樣的景象,數(shù)據(jù)庫(kù)技術(shù)已經(jīng)成為了????的基?。在數(shù)據(jù)管理和數(shù)據(jù)庫(kù)相關(guān)的??,Apple給出的選擇是CoreData。正如在簡(jiǎn)介中所提到的那樣,CoreData其實(shí)并不是?個(gè)傳統(tǒng)意義上的數(shù)據(jù)庫(kù),?是?套對(duì)象圖管理系統(tǒng)。這套系統(tǒng)默認(rèn)使?作為底層 對(duì)象管理機(jī)制。這讓我們對(duì)于數(shù)據(jù)對(duì)象的和都能夠?效?進(jìn)?。從這?點(diǎn)上來(lái)說(shuō),CoreData與單純的數(shù)據(jù)庫(kù)相?,實(shí)在是強(qiáng)?得多。但是能?越?,責(zé)任也越?。如果使?不當(dāng),CoreData不但不能為你提供良好的數(shù)據(jù)和的性能,甚?會(huì)連最基本的操作都難以保證。在這種情況下,CoreData將不再是你開(kāi)發(fā)多,所以真正想要精通CoreData并完全發(fā)揮它的效能并不是很容易的事情。Apple在iOS的很多原?應(yīng)?中?量使?了CoreData,?如照?、?樂(lè)和iBooks等,并且事實(shí)證明它們都出?地完成了任務(wù)。在國(guó)外,也有很多開(kāi)發(fā)者使?CoreData作為應(yīng)?程序的數(shù)據(jù)層和持久化的選擇。相?于其他第三?的解決?案,CoreData不需要引?額外的框架,也相對(duì)穩(wěn)定可靠。但是在國(guó)內(nèi),現(xiàn)在使?這項(xiàng)技術(shù)的開(kāi)發(fā)者較少,?家對(duì)CoreData的研究也普遍沒(méi)有國(guó)外深?,這導(dǎo)致了提到CoreData很多?會(huì)不?覺(jué)地抗拒和躲避。將CoreData的使??法和最佳實(shí)踐以更容易理解的?式帶給國(guó)內(nèi)開(kāi)發(fā)者,促進(jìn)?家接觸CoreData的架構(gòu)和思你充分理解這些例?的含義后,你才可能在實(shí)際使?時(shí)作出正確的判斷。另外,CoreData的靈活性是?把雙刃劍,當(dāng)你選擇了的上下?以及協(xié)調(diào)器時(shí),也意味著你為項(xiàng)?引?了的復(fù)雜度。盡可能在能夠滿?需求的前提下,選擇最簡(jiǎn)單的CoreData棧設(shè)置,是?效正確使?CoreData本書(shū)原版的兩位作者有著多年的CoreData使?經(jīng)驗(yàn)。FlorianKugler是objc.io?,曾經(jīng)為objc.io撰寫(xiě)了很多CoreData相關(guān)的?章,深受讀者喜愛(ài)。DanielEggert曾供職于Apple,幫助Apple將照?應(yīng)?遷移到CoreData框架內(nèi)。他們的努?讓CoreData這個(gè)看起來(lái)有些“可怕的框架變得平易近?,籍此我們可以?窺CoreData的究竟。不過(guò)不論是原作在所難免。如果您在閱讀時(shí)發(fā)現(xiàn)了問(wèn)題,可以給我們發(fā)郵件,或是在本書(shū)issue??提出,我3在本章中,創(chuàng)建?個(gè)簡(jiǎn)單的使?CoreData的?例程序。在這個(gè)過(guò)程中,我們會(huì)介紹多值得?談的內(nèi)容。不過(guò)請(qǐng)放?,后?會(huì)詳細(xì)回顧這些內(nèi)容。本章會(huì)介紹這個(gè)?例程序中與CoreData相關(guān)的所有??的內(nèi)容。請(qǐng)注意這并不是?個(gè)從頭開(kāi)始?步?步教你如何創(chuàng)建整個(gè)應(yīng)?的。我們推薦你看?下在 這個(gè)?例應(yīng)?程序包括?個(gè)簡(jiǎn)單的tableview和底部的實(shí)時(shí)頭拍攝的內(nèi)容。拍攝?張照?后,我們從照?中提取出它的?組主?。然后這些配??案(我們稱其為“mood”),并相應(yīng)地更新tableview。?例應(yīng)?程序-“?個(gè)基本的CoreData棧由四個(gè)主要部分組成:托管對(duì)象(managedobjects)(NSManagedObject),托管對(duì)象上下?(managedobjectcontext)(NSManagedObjectContext),持久化存儲(chǔ)協(xié)調(diào)器(persistentstorecoordinator)??????托管對(duì)象位于這張圖的最上層,它是架構(gòu)?最有趣的部分,同時(shí)也是我們的數(shù)據(jù)模型在這個(gè)CoreData其他的部分進(jìn)?集成。每個(gè)Moodmood,也就是???相機(jī)拍攝我們的mood對(duì)象是被CoreData托管的對(duì)象。也就是說(shuō),它們存在于?個(gè)特定的上下? 的這個(gè)簡(jiǎn)單例?,我們不?太關(guān)?持久化協(xié)調(diào)器或者持久化,因?yàn)镹SPersistentContainer這個(gè)輔助類會(huì)幫助我們把它們都設(shè)置好??梢赃@么說(shuō),默認(rèn)情況下CoreData會(huì)使??個(gè)類型的持久化,也就是說(shuō)你的數(shù)據(jù)在底層實(shí)際上會(huì)被在?個(gè)數(shù)據(jù)庫(kù)?。CoreData也提供其他的 類型(?如XML,?進(jìn)制數(shù)據(jù),內(nèi)存),但是現(xiàn)在我們不需要考慮其他的類型。 CoreData 結(jié)構(gòu)化的數(shù)據(jù)。所以為了使?CoreData,我們?先需要?jiǎng)?chuàng)建?個(gè)數(shù)據(jù)模型(或者是?綱(schema),如果你樂(lè)意這么叫它的話來(lái)描述我們的數(shù)據(jù)結(jié)構(gòu)。.xcdatamodeld?件會(huì)更容易。在你開(kāi)始?Xcode模板創(chuàng)建新的iOS或macOS應(yīng)?程序時(shí)候,你可以在File>New彈出的菜單?的CoreData部分中選擇“DataModel”來(lái)創(chuàng)建?個(gè)數(shù)據(jù)模型。如果你在第?次創(chuàng)建項(xiàng)?時(shí)勾上了“UseCoreData”這個(gè)選項(xiàng),Xcode將為你創(chuàng)建?個(gè)空事實(shí)上,你并不需要通過(guò)勾上“UseCoreData”選項(xiàng)來(lái)使?CoreData相反,我們建議你不要如果你在Xcode的projectnavigator?選中了數(shù)據(jù)模型?件,Xcode的數(shù)據(jù)模型編輯器就會(huì)實(shí)體(entity)是數(shù)據(jù)模型的基?。正因?yàn)槿绱耍?個(gè)實(shí)體應(yīng)該代表你的應(yīng)?程序?有意義的?部分?jǐn)?shù)據(jù)。例如,在我們的例??,我們創(chuàng)建了?個(gè)叫Mood的實(shí)體,它有兩個(gè)屬性:?個(gè)代 著實(shí)現(xiàn)了NSCoding協(xié)議的對(duì)象或者是提供了?定義值轉(zhuǎn)換器(valuetransformer對(duì)于(被稱為colors)。屬性的名稱應(yīng)該以?寫(xiě)字?開(kāi)頭,就像類或者結(jié)構(gòu)體?的屬性?樣。colors屬性是?個(gè)數(shù)組,??都是UIColor對(duì)象,因?yàn)镹SArray和UIColor已經(jīng)遵循了NSCoding協(xié)議,在XcodeMood兩個(gè)屬性都有的?些選項(xiàng)可以讓我們調(diào)整。我們把date屬性標(biāo)記為必選的(non-optional)和可索引的(indexed)。colors數(shù)組也標(biāo)記為必選屬性。Data會(huì)在底層數(shù)據(jù)庫(kù)表?創(chuàng)建?個(gè)索引。索引可以加速這個(gè)屬性的搜索和排序,但代深?探討這個(gè)。""date"????Mood"colors"????:????:Mood實(shí)體的托管對(duì)象?類。實(shí)體只是描?個(gè)好的實(shí)踐是按它們所代表的東西來(lái)命名這些類,并且不?添加類似Entity這樣的后綴。?如,我們的類直接叫Mood?不是MoodEntity。實(shí)體和類都叫Mood,?常完美。我們的?nalclassMood:{@NSManaged?leprivate(set)vardate:}修飾Mood類屬性的@NSManaged告訴編譯器這些屬性將由CoreData來(lái)實(shí)現(xiàn)。CoreData??種很不同的?式來(lái)實(shí)現(xiàn)它們,我們會(huì)在第?部分?詳細(xì)談?wù)撨@部分內(nèi)容。?leprivate(set)這個(gè)控制修飾符表?這兩個(gè)屬性都是公開(kāi)只讀的。CoreData其實(shí)并不強(qiáng)為了能讓CoreData識(shí)別我們的MoodMood實(shí)體相關(guān)聯(lián),我們?cè)谀P途庉嬈?選中這個(gè)實(shí)體,然后在datamodelinspector?輸?它的類名?,F(xiàn)在我們有第?個(gè)版本的數(shù)據(jù)模型和Mood類了,我們可以使?NSPersistentContainer來(lái)設(shè)置?個(gè)基本的CoreData棧。使?如下的?法來(lái)創(chuàng)建這個(gè)容器,從中我們可以獲取將在整個(gè)app?都被使?的托管對(duì)象上下?:funccreateMoodyContainer(completion:{container.loadPersistentStores{_,errorinDispatchQueue.main.async{completion(container)}}}查找對(duì)應(yīng)的數(shù)據(jù)模型,所以它應(yīng)該和你的.xcdatamodeldbundle的?件名?致。接下來(lái),我們調(diào)?容器的loadPersistentStores?法來(lái)嘗試打開(kāi)底層的數(shù)據(jù)庫(kù)?件。如果數(shù)據(jù)庫(kù)?件還不存在,CoreData會(huì)根據(jù)你在數(shù)據(jù)模型?定義的?綱(schema)來(lái)?成它。因?yàn)槌志没瘋?在我們的例??,以及?多數(shù)真實(shí)世界情況下,只會(huì)有?個(gè))是異步加載的,?旦?個(gè)被加載完成,我們的回調(diào)就會(huì)被執(zhí)?。如果發(fā)?了?個(gè)錯(cuò)誤,我們現(xiàn)在就->{createMoodyContainer{containeringuardletvc=storyboard?.instantiateViewController(withIdenti?er:as?vc.managedObjectContext=container.viewContextself.window?.rootViewController=vc}}}?旦我們接收持久化容器參數(shù)的回調(diào)被執(zhí)?,我們就把這個(gè)容器在?個(gè)屬性?。然后我們把應(yīng)?程序啟動(dòng)時(shí)加載的初始viewcontroller只是在我們加載完前?來(lái)占位的替換成我管對(duì)象上下?賦值給它,并把它設(shè)置成window的rootviewcontroller。為了?便在viewcontroller?使?這個(gè)托管對(duì)象上下?,我們?cè)趹?yīng)?程序?把這個(gè)上下?對(duì)象傳遞給第?個(gè)viewcontroller,然后通過(guò)它再傳遞給視圖層次?其他需要這個(gè)上下?給了MoodTableViewController:sender:Any?){caseguardletnc=segue.destinationas?UINavigationController,as?else{fatalError("wrongviewcontrollertype")}//}}//controller來(lái)拿到MoodsTableViewController實(shí)例。如果你對(duì)segueIdenti?er(for:這個(gè)?法的由來(lái)感到好奇,可以參考WWDC2015的SwiftinPractice這個(gè)session,我們參考了??的這個(gè)模式。這是在Swift?使?協(xié)議擴(kuò)展(protocol為了展?mood對(duì)象-雖然我們現(xiàn)在還沒(méi)有數(shù)據(jù),我們可以先劇透?點(diǎn)-我們會(huì)使?tableview與CoreData的NSFetchedResultsController的組合來(lái)顯?數(shù)據(jù)。這個(gè)類會(huì) 顧名思義,?個(gè)獲取(Fetch)請(qǐng)求描述了哪些數(shù)據(jù)需要被從持久化 需要的重要?點(diǎn)是:每次你執(zhí)??個(gè)獲取請(qǐng)求,CoreData會(huì)穿過(guò)整個(gè)CoreData棧,直到?件系統(tǒng)。按照API約定,獲取請(qǐng)求就是往返的:從上下?,經(jīng)過(guò)持久化協(xié)調(diào)器和持久化,降?,然后原路返回。的Mood實(shí)例,并按它們的創(chuàng)建時(shí)間降序排列(我們很快會(huì)整理這部分代碼):request.sortDescriptors=[sortDescriptor]這個(gè)entityName參數(shù)是我們的Mood實(shí)體在數(shù)據(jù)模型?的名稱。?fetchBatchSize屬性告訴CoreData?次只獲取特定的數(shù)量的mood對(duì)象。這背后其實(shí)發(fā)?了許多“魔法”;我們會(huì)更容易使?和。題代碼的調(diào)?(將針對(duì)特定領(lǐng)域問(wèn)題代碼抽象化成較少的程式碼,例如將代碼封裝成在在在Swift中,協(xié)議扮演了核???。我們會(huì)給Mood模型添加并實(shí)現(xiàn)?個(gè)協(xié)議。事實(shí)上,我們后?添加的模型類都會(huì)實(shí)現(xiàn)這個(gè)協(xié)議我們建議在你的模型類?也這么做:}利?Swift的協(xié)議擴(kuò)展來(lái)為defaultSortDescriptors添加?個(gè)默認(rèn)的實(shí)現(xiàn),同時(shí)也作為這個(gè)實(shí)體的?個(gè)使?默認(rèn)排序描述符的獲取請(qǐng)求的計(jì)算屬性(computedproperty):}request.sortDescriptors=defaultSortDescriptors}}}我們希望Mood的實(shí)例默認(rèn)按?期排序(就像在我們之前創(chuàng)建的獲取請(qǐng)求?做的那樣):}}request.fetchBatchSize=20我們后?會(huì)以這個(gè)模式為基礎(chǔ),給Managed協(xié)議添加的便利?法-?如,創(chuàng)建獲取請(qǐng)求的時(shí)候指定謂詞(predicate或者是搜索這個(gè)類型的對(duì)象。你可以參考?例代碼?的Managed得依賴的良好基礎(chǔ)。隨著我們的app變得越來(lái)越復(fù)雜,我們會(huì)地使?這個(gè)模式。FetchedResults我們使?NSFetchedResultsController類來(lái)協(xié)調(diào)模型和視圖。在我們的例??,我們?它來(lái)其他場(chǎng)景,?如在使?collectionview的時(shí)候。使?fetchedresultscontrollers的主要優(yōu)勢(shì)是:我們不是直接執(zhí)?獲取請(qǐng)求然后把結(jié)果交給tableview,?是在當(dāng)?shù)讓訑?shù)據(jù)有變化的時(shí)候,它能通知我們,讓我們很容易地更新tableview。為了做到這?點(diǎn),fetchedresultscontrollers 上下?在它之中的數(shù)據(jù)發(fā)?改變的時(shí)候所發(fā)出(修改和保存數(shù)據(jù)這?章會(huì)有關(guān)于這??的內(nèi)容)。fetchedresultscontrollers會(huì)根據(jù)底層獲取請(qǐng)求的排序,計(jì)算出哪些對(duì)象的位置發(fā)?了變化,哪些對(duì)象是新插?的等等,然后把這些改動(dòng)報(bào)告給它的:FetchedFetchedResultsFetchedResultsControllerTableViewTableViewTable提到的獲取請(qǐng)求來(lái)創(chuàng)建?個(gè)fetchedresultscontroller://request.fetchBatchSize=20managedObjectContext:managedObjectContext,sectionNameKeyPath:nil,cacheName:nil)//}我們可以在viewcontroller的類?直接實(shí)現(xiàn)上?的這些?法。但是這樣的模板代碼會(huì)把viewcontroller弄得很亂,?且我們不得不在每個(gè)需要使?fetchedresultscontroller的viewresultscontroller的?法的實(shí)現(xiàn)封裝進(jìn)可以復(fù)?的?個(gè)類?,同時(shí)這個(gè)類可以作為table//fetchedResultsController:frc,delegate:self)}以及tableview的數(shù)據(jù)源。然后它調(diào)?performFetch(_:)?法從持久化中加載這些數(shù)據(jù)。由于這個(gè)?法可能會(huì)拋出錯(cuò)誤,所以我們?cè)谒?加了try!來(lái)讓它盡早的,因?yàn)檫@{requiredinit(tableView:UITableView,cellIdenti?er:String,delegate:Delegate){self.cellIdenti?er=cellIdenti?erself.delegate=delegatefetchedResultsController.delegate=selftableView.dataSource=self}//} ?實(shí)際地顯?出數(shù)據(jù)。為此,我們?cè)?定義的TableViewDataSource類?實(shí)現(xiàn)必要的兩個(gè)->{}cellForRowAtindexPath:IndexPath)->{guardletcelltableView.dequeueReusableCell(withIdenti?er:cellIdenti?er,for:indexPath)as?Cell}controller是通過(guò)傳遞Mood實(shí)例給cell的con?gure?法來(lái)實(shí)現(xiàn)這個(gè)?法的。}}被持久化,除?你顯式地調(diào)?上下?的save?法來(lái)保存它們。在我們的?例app?,插?新的mood對(duì)象是通過(guò)拍攝新的照?完成的。這?我們不會(huì)包含所有必需的?CoreData的代碼,其他的代碼你還是可以參考在 當(dāng)??拍攝新照?的時(shí)候,我們通過(guò)調(diào)?在NSEntityDescription上的的顏?賦值給它,最后調(diào)?上下?的save?法:context)as?Moodmood.colors=結(jié)果向下轉(zhuǎn)換成Mood類型。然后,我們希望colors是公開(kāi)只讀的。最后,我們其實(shí)應(yīng)該要去處理save可能拋出的錯(cuò)誤。代它的類型。我們利?在Managed協(xié)議中引?的靜態(tài)entityName屬性來(lái)實(shí)現(xiàn)這個(gè)功能:funcinsertObject<A:NSManagedObject>()->AwhereA:Managed{A.entityName,into:self)as?}}型。編譯器會(huì)從?法的類型注解(typeannotation)?動(dòng)推斷出我們嘗試插?的對(duì)象類型:letmood:Mood=//image:UIImage)->Mood{mood.colors=image.moodColorsmood.date=}}do{}{}}_=}}}第?個(gè)?法,saveOrRollback,直接捕獲了調(diào)?save?法可能拋出的異常,并在出錯(cuò)的時(shí)候回滾掛起的改動(dòng)。也就是說(shuō),它直接扔掉了那些沒(méi)有保存的數(shù)據(jù)。對(duì)于我們的?例app??,然?具體到你能否這么做,還是取決于你使?CoreData的?式,也許你需要更精密的處理。傳?的函數(shù),然后保存上下?。調(diào)?perform?法能確保我們是從正確的隊(duì)列?上下?和在的話,你只需要把這種做法當(dāng)成?個(gè)最佳實(shí)踐模式即可:始終把和CoreData對(duì)象交互的代碼封裝進(jìn)類似的?個(gè)block?。funcdidCapture(_image:_=Mood.insert(into:self.managedObjectContext,image:}}在整個(gè)項(xiàng)??,我們可以復(fù)?這些輔助?法來(lái)編寫(xiě)更?凈、可讀的代碼這并不需要引?什么為了演?刪除對(duì)象的最佳實(shí)踐,我們會(huì)添加?個(gè)detailviewcontroller,它會(huì)顯?關(guān)于單個(gè)你在tableview中選擇?個(gè)mood的時(shí)候,detailviewcontroller可以被推?導(dǎo)航棧中。新創(chuàng)建的viewcontroller的?個(gè)屬性值:caseas?vc.mood=mood}}@IBActionfuncdeleteMood(_sender:{mood.managedObjectContext?.performChanges}}請(qǐng)注意我們是如何使?mood對(duì)象的managedObjectContext屬性的。在這個(gè)view為了能讓刪除?效,我們調(diào)?之前介紹的mood對(duì)象的上下?的performChanges輔助?法。接著我們?cè)赽lock?,調(diào)?了delete?法,并把mood對(duì)象作為參數(shù)傳遞了進(jìn)去,最后performChanges(_:)這個(gè)輔助?法執(zhí)?完刪除操作后會(huì)保存上下?。很?然,如果mood對(duì)象被刪除了,讓detailviewcontroller還在棧?并沒(méi)有什么意義。最直接的做法是,在我們刪除mood對(duì)象的同時(shí)彈出這個(gè)detailviewcontroller。不過(guò),要采取?種更泛?的?法。這種?法同樣能處理mood對(duì)象可能在?絡(luò)同步操作時(shí)被刪除的我們要使?的?法和fetchedresultscontroller中的響應(yīng)式?法?樣:“對(duì)象已改變”為了達(dá)到這個(gè)?的,我們構(gòu)建了?個(gè)托管對(duì)象觀察者(managedobjectobserver),它接受兩個(gè)?nalclassManagedObjectObserverinit?(object:changeHandler:@esca(ChangeType)->{//}}在我們的detailviewcontrollervarmood:Mood!didSetguardtype==.deleteelse{return}}}}我們?cè)趍ood這個(gè)屬性的didSet屬性觀察?法?初始化了?個(gè)觀察者對(duì)象,并將它作為?個(gè)實(shí)例變量保存。當(dāng)被觀察的mood對(duì)象被刪除的時(shí)候,這個(gè)閉包會(huì)以.delete作為變化類型參們都會(huì)收到對(duì)象被刪除。ManagedObjectObserver類了“對(duì)象已改變”變化的時(shí)候都會(huì)發(fā)出這個(gè)通知。它為我們感的托管對(duì)象所在的上下?了這個(gè)通知,當(dāng)收到通知之后,它會(huì)遍歷通知的userinfo字典來(lái)檢查被觀察的對(duì)象是否被刪除:enumChangeTypecasecase}changeHandler: (ChangeType)->{token=moc.addObjectsDidChangeNoti?cationObserver{[weakself]notein}}{}innote:ObjectsDidChangeNoti?cation)->{ifnote.invalidatedAllObjects||{}}}}在觀察者的代碼?有兩件有趣的事情值得注意:?先,為了觀察上下?,我們把NSNoti?cation的userinfo字典?的松散的類型信息的數(shù)據(jù)做了強(qiáng)類型封裝。這樣能讓代碼 對(duì)于任意的持久化條?,在?個(gè)托管對(duì)象上下??只會(huì)存在?個(gè)單獨(dú)的托管對(duì)象。我們會(huì)在第?部分了解的細(xì)節(jié)內(nèi)容。然后我們?yōu)檫@個(gè)實(shí)體創(chuàng)建了對(duì)應(yīng)的NSManagedObject?類。最后我們使?了NSPersistentContainer來(lái)設(shè)置CoreData棧。?tableview來(lái)展?。我們還增加了插?和刪除mood對(duì)象的功能。我們使?了響應(yīng)式(reactive)的?法在數(shù)據(jù)發(fā)?變化的時(shí)候來(lái)更新我們的UI:對(duì)于tableview,我們使?了CoreData的fetchedresultscontroller;對(duì)于detailview,我們使?了??實(shí)現(xiàn)的基于上下?變→→復(fù)?,保持viewcontroller精簡(jiǎn),也更符合Swift的類型安全特性?!?dāng)前展?的對(duì)象被刪除或者改變的時(shí)候,確保你的UI能被更新。我們推薦使?響應(yīng)式你可以通過(guò)觀察上下?的“已經(jīng)改變”來(lái)實(shí)現(xiàn)類似的模式。iOS10/macOS10.12之前的版本需要注意本,那么設(shè)置CoreData棧需要的?些?動(dòng)步驟:funccreateViewContext()->NSManagedObjectContext.mergedModel(from:con?gurationName:nil,at:storeURL,options:nil)letcontext=}?先,我們獲取了托管對(duì)象模型所在的bundle。這?我們調(diào)?了Bundle(for:)?法,這樣?NSManagedObjectModel的輔助?法mergedModel(from:)來(lái)加載數(shù)據(jù)模型。這個(gè)? StoreType的持久化 。的位置是由私有的storeURL常量指定的,通常它位 CoreData會(huì)在這個(gè)位置創(chuàng)建?個(gè)新的數(shù)據(jù)庫(kù)。上下?是綁定到主隊(duì)列的,也就是我們處理所有UI交互的地?。我們可以從UI代碼的任何地?安全地這個(gè)上下?和其中的托管對(duì)象。我們會(huì)在在CoreData中使?多個(gè)上下?這?章中介紹關(guān)于這部分的內(nèi)容。4在本章中,我們通過(guò)添加兩個(gè)新的實(shí)體:Country(國(guó)家Continent(?陸來(lái)擴(kuò)展我們的數(shù)及什么時(shí)候不應(yīng)該使?它們。在這之后我們會(huì)建?這三個(gè)實(shí)體之間的關(guān)系。關(guān)系是CoreData的?個(gè)關(guān)鍵特性,使?關(guān)系把每個(gè)mood和?個(gè)country、以及每個(gè)country和?個(gè)continent聯(lián)系起來(lái)。修改數(shù)據(jù)模型會(huì)導(dǎo)致p在下次運(yùn)?時(shí)。但只要你還處于開(kāi)發(fā)階段?且沒(méi)有分發(fā)p,那么你可以直接刪除設(shè)備或模擬器?舊版本的p,這樣你就可以繼續(xù)?作了。為簡(jiǎn)單起?,在,新的實(shí)體都有?個(gè)countrycontinent的ISO3166編碼的屬性。我們把這個(gè)屬性命名為Date的updatedAt屬性,我們之后在tableview?會(huì)使?它進(jìn)?排序。@NSManagedvarupdatedAt:}}setnumericISO3166Code=}}}它標(biāo)記為?leprivate。我們?cè)黾恿?個(gè)?來(lái)公開(kāi)的iso3166Code的計(jì)算屬性(computedproperty),它可以使?枚舉類型ISO3166.Country來(lái)進(jìn)?(私有的)設(shè)置和。ISO3166.Country是使?三個(gè)字?的國(guó)家碼來(lái)定義的?個(gè)枚舉選項(xiàng):structISO3166{caseguy=328casepol=616caseltu=440casenic=558//}@NSManagedvarupdatedAt:getguardletc=ISO3166.Continent(rawValue:return}numericISO3166Code=}}}當(dāng)然,我們也讓Country和Continent類遵循了我們?章?介紹過(guò)的Managed協(xié)議。這}}接下來(lái)是把mood和它拍攝時(shí)對(duì)應(yīng)的country聯(lián)系起來(lái)。為了做到這?點(diǎn),我們希望能 個(gè)mood的地理位置信息。給Mood實(shí)體添加兩個(gè)新的屬性:latitude(緯度)和 ?個(gè)CLLocation對(duì)象,但是這樣會(huì)很浪費(fèi)空 原始的latitude和longitude值,并在Mood類上 ?個(gè)location屬性,?這些值我們就可以構(gòu)造出?個(gè)CLLocation對(duì)//}@NSManaged?leprivatevarlatitude:@NSManaged?leprivatevarlongitude:}在Mood類?,須要使?NSNumber類型來(lái)表?latitude和longitude屬性,因?yàn)槲覀兿M鼈兪荗ptional。我們其實(shí)更愿意這些屬性為Double?,但是這個(gè)類型?法在Objective-C?表?,所以沒(méi)辦法和@NSManaged?起?作。由于我們想在app?同時(shí)查詢country和continent(并讓它們?起顯?在?個(gè)tableview?),利?CoreData中?個(gè)名為?實(shí)體的特性。體的屬性和關(guān)系。雖然這聽(tīng)起來(lái)和?類化(subclassing)很相似,但是理解它們之間的差異是很對(duì)象。在我們的例??,我們想要??個(gè)tableview來(lái)將countrycontinent?(稍后我們還會(huì)添加只顯?它們其中?個(gè)的選項(xiàng))。我們可以通過(guò)添加?個(gè)抽象的Region實(shí)共享相同的屬性(也就是numericISO3166Code和updatedAt),我們可以把它們移?到它們的Region實(shí)體創(chuàng)建?個(gè)會(huì)同時(shí)返回country和continent的獲取請(qǐng)求,我們還必須定義?個(gè)Region類:CountryContinentRegion實(shí)體的?實(shí)體,但是我們沒(méi)有讓Country和Continent 只添加?次所有的共同屬性的做法看上去很誘?,但是這樣會(huì)有嚴(yán)重的。共同?實(shí)體的?實(shí)體將共享?個(gè)公共的數(shù)據(jù)庫(kù)表,所有兄弟實(shí)體(siblingentities)的所有屬性都會(huì)被合并進(jìn)這個(gè)表?。盡管CoreData在與你交互的層級(jí)上隱藏了這?點(diǎn),但CoreData將不得不從?個(gè)巨型數(shù)據(jù)庫(kù)的結(jié)構(gòu),可以參考關(guān)系型數(shù)據(jù)庫(kù)基礎(chǔ)知識(shí)和SQL這?章節(jié)的內(nèi)容。)CoreDataCoreData把?實(shí)體們合并到?個(gè)共同的表?的?可以將?實(shí)體想象成是?種給實(shí)體添加?個(gè)“類型枚舉的取巧的?法,它可以?來(lái)告訴你?個(gè)實(shí)例的類型是“A”還是“B”。當(dāng)你猶豫是否要使??實(shí)體的時(shí)候,請(qǐng)這么思考?下:要是把擁體的做法是完全可以接受的。但是在Swift?,使??個(gè)共同的協(xié)議?不是?類化的做法可能??個(gè)例?來(lái)說(shuō)明這點(diǎn),我們定義?個(gè)名為L(zhǎng)ocalizedStringConvertible的協(xié)議,使?這個(gè)協(xié)議來(lái)?持使?country或continent來(lái)配置?個(gè)regiontableviewcell:protocolLocalizedStringConvertiblevarlocalizedDescription:String{get}現(xiàn)在我們可以讓Country和Continent都遵循這個(gè)協(xié)議,之后我們就可以在viewlocalizedDescription這個(gè)屬性來(lái)配置tableviewcellextensionCountry:LocalizedStringConvertiblevarlocalizedDescription:Stringreturn}}}}由于我們只了country的ISO編碼,我們可以使?NSLocale來(lái)顯?country的本地化名我們希望能夠使?tableview向??展??個(gè)區(qū)域的列表。如果??選擇了?個(gè)country,我們會(huì)顯?在這個(gè)country?拍攝的mood。如果??選擇了?個(gè)continent,展?這個(gè)列表的選項(xiàng),?來(lái)只顯?country或continent,或者同時(shí)顯?它們。實(shí)體之間的關(guān)系很簡(jiǎn)單:?個(gè)continent包含多個(gè)country,?每個(gè)country只屬于?個(gè)continent?少在我們簡(jiǎn)化版的世界?是這樣的)。每個(gè)country可以有多個(gè)mood,?每個(gè))我們通常所說(shuō)的?個(gè)“?對(duì)多的關(guān)系,其實(shí)是由模型?的兩個(gè)關(guān)系組成的:每個(gè)?向各?個(gè)。要建?Continent和Country之間的關(guān)系,我們實(shí)際上在模型編輯器定義了兩個(gè)關(guān)系:?個(gè)從Continent到Country,另?個(gè)從Country到Continent。Continent上的關(guān)系被叫做因?yàn)樗恰皩?duì)?的)。類似地,我們從Country到Mood建?了?個(gè)叫moods的“對(duì)多關(guān)系,以及從Mood到Country建?了?個(gè)叫country的“對(duì)?”關(guān)系:CoreData會(huì)?動(dòng)地更新反向關(guān)系:當(dāng)我們?cè)O(shè)置Countrycontinent時(shí),在Continent上對(duì)@NSManagedpublic?leprivate(set)varcountry://}@NSManaged?leprivate(set)varmoods:@NSManaged?leprivate(set)varcontinent://}@NSManaged?leprivate(set)varcountries://}的mood將會(huì)與?個(gè)未知country進(jìn)?關(guān)聯(lián)(我們?cè)贗SO3166.Country枚舉?定義了?個(gè)Unknown),這個(gè)未知的country不屬于任何?個(gè)continent。在數(shù)據(jù)模型?定義的所有這些關(guān)系并不是必須要加到你的NSManagedObject型?定義了反向關(guān)系,就算?類?沒(méi)有定義它們,CoreData也會(huì)?作得很好。在上?的例??,我們只?了“?對(duì)多的關(guān)系。除此之外,你經(jīng)常想要在兩個(gè)實(shí)體之間創(chuàng)建的創(chuàng)建?個(gè)“?對(duì)?”關(guān)系和我們?cè)谏?創(chuàng)建的“?對(duì)多”關(guān)系?常類似:創(chuàng)建關(guān)系和它對(duì)應(yīng)的反向上的關(guān)系類型設(shè)置為“對(duì)多來(lái)創(chuàng)建的。和“?對(duì)多關(guān)系?樣,對(duì)于“?對(duì)?和“多對(duì)多關(guān)系這兩種情況,CoreData也會(huì)?動(dòng)更新它們的反向關(guān)系。在內(nèi)部的SQL?,“多對(duì)多”關(guān)系要?“?對(duì)?”或者“?對(duì)多”關(guān)系更復(fù)雜。這是因?yàn)樗皩?duì)多關(guān)系有兩種形式:?序的和有序的。默認(rèn)情況下,“對(duì)多關(guān)系沒(méi)有特定的順序,通過(guò)它們的數(shù)據(jù)類型就可以看出來(lái)。標(biāo)準(zhǔn)的“對(duì)多”關(guān)系是?Set類型的屬性來(lái)表?的。這可以保證包?為。有序的關(guān)系是?NSOrderedSet類型的屬性來(lái)表?的,它可以保證包含對(duì)象的唯?性以及?個(gè)特定的順序。在下?有關(guān)更改“對(duì)多關(guān)系的?節(jié)?我們可以了解在有序關(guān)系?插?和移)通過(guò)為同?類實(shí)體添加parent和children關(guān)系來(lái)創(chuàng)建?個(gè)樹(shù)形結(jié)構(gòu)。家或者是不同國(guó)家的公?(citizen)和居?(resident)。我們可以?稱為residents和citizens的兩個(gè)“?對(duì)多”關(guān)系以及對(duì)應(yīng)的反向關(guān)系residentOf和citizenOf來(lái)建模這種情況。最后,你還可以創(chuàng)建單向關(guān)系(unidirectionalrelationships),也就是沒(méi)有對(duì)應(yīng)的反向關(guān)系的關(guān)(referentialintegrityproblem)。這意味著在數(shù)據(jù)庫(kù)中的?個(gè)條?可能指向另?個(gè)已經(jīng)不存在的慮這個(gè)例?:我們有Message和User兩種實(shí)體,它們是通過(guò)從Message到User的?個(gè)叫sender的“對(duì)?”關(guān)系聯(lián)系起來(lái)的。如果我們百分百確信我們不會(huì)刪除User對(duì)象,那么我們可以考慮省去從User到Message的反向關(guān)系messages來(lái)避免更新這個(gè)關(guān)系的開(kāi)銷,反正我們不會(huì)使?它。但要注意,這可能是?個(gè)典型的過(guò)早優(yōu)化(prematureoptimization)的例??定要?先檢查這個(gè)關(guān)系是否真的會(huì)導(dǎo)致性能問(wèn)題,再?zèng)Q定是否要做出這樣的優(yōu)化。在我們的例??,我們想要在?個(gè)mood被創(chuàng)建時(shí)設(shè)置它的country,同時(shí)我們希望country被創(chuàng)建時(shí)設(shè)置它的continent。對(duì)于前者,我們可以修改Mood類的靜態(tài)便捷?法來(lái)設(shè)置publicstaticfuncinsert(intocontext:image:UIImage,location:CLLocation?,cemark:CLcemark?)->{letmood:Mood=context.insertObject()mood.colors=image.moodColorsmood.date=Date()ifletcoord=location?.coordinatemood.latitude=NSNumber(value:coord.latitude)mood.longitude=NSNumber(value:coord.longitude)}letisoCode=cemark?.isoCountryCode??letisoCountry=ISO3166.Country.fromISO3166(isoCode)mood.country=Country.?ndOrCreate(for:isoCountry,in:context)returnmood}在我們把CLcemark表?的country代碼轉(zhuǎn)換成ISO3166.Country值后(如果代碼不能被識(shí)別,這個(gè)值會(huì)是.unknown),我們調(diào)?Country類的?ndOrCreate(for:in:)?法來(lái)獲取對(duì)應(yīng)的country對(duì)象。這個(gè)輔助?檢查該country是否已存在,如果不存在,則創(chuàng)建它:staticfunc?ndOrCreate(forisoCountry:incontext:NSManagedObjectContext)->{letpredicate=NSPredicate(format:"%K==%d",letcountry=?ndOrCreate(in:context,matching:predicate)$0.iso3166Code=$0.updatedAt=$0.continent=Continent.?ndOrCreateContinent(for:in:}}在這個(gè)?法?,我們?先創(chuàng)建了?個(gè)謂詞(predicate),通過(guò)ISO代碼來(lái)過(guò)濾所有的country以便找到具有特定代碼的country。謂詞是?種?于過(guò)濾獲取請(qǐng)求結(jié)果的強(qiáng)?機(jī)制,在專然后我們把這個(gè)謂詞傳給實(shí)際完成繁重?作的?ndOrCreate(in:matching:)?法。這個(gè)輔助?法是定義在Managed協(xié)議的?個(gè)擴(kuò)展?的:matchingpredicate:NSPredicate,con?gure:(Self)->())->{matching:predicate)else{}}}讓我們來(lái)?步?步地分析:?先,我們調(diào)?了?ndOrFetch(in:matching:)?法。這個(gè)?檢查我們要尋找的對(duì)象是否已經(jīng)在上下??過(guò),如果沒(méi)有,我們會(huì)嘗試使??個(gè)獲取請(qǐng)求來(lái)加載它。假如這個(gè)對(duì)象存在于CoreData?,那么它將作為該獲取請(qǐng)求的結(jié)果被返回。如?ndOrFetch(in:matching:)?法?使?的兩步法執(zhí)?獲取請(qǐng)求之前先在內(nèi)存?檢查,是?對(duì)象數(shù)組,也要?執(zhí)??個(gè)獲取請(qǐng)求快得多。在性能這章?對(duì)這??的內(nèi)容進(jìn)?探matchingpredicate:NSPredicate)->Self?{matching:predicate)elserequest.predicate=predicaterequest.fetchLimit=1}}}這?值得?提的是,我們使?了Managed協(xié)議上的兩個(gè)輔助?法:其中materializedObject(in:matching:)? staticfuncincontext:NSManagedObjectContext,{guardletresult=objectas?Self,}}}管對(duì)象實(shí)例(有關(guān)惰值的詳情可以參考數(shù)據(jù)這?章)。如果我們?cè)噲D在惰值上執(zhí)?我們失的數(shù)據(jù)這種開(kāi)銷可能是?常昂貴的。extensionManagedwhereSelf:NSManagedObjectstaticfuncfetch(incontext:->{letrequest=NSFetchRequest<Self>(entityName:}}現(xiàn)在,讓我們回到修改Mood類之前所試圖完成的?標(biāo)上來(lái)。我們已經(jīng)擴(kuò)展了Mood的靜態(tài)輔助?法,現(xiàn)在我們能通過(guò)查找?個(gè)已存在的country,或者是創(chuàng)建?個(gè)新的country對(duì)象,并?它來(lái)設(shè)置Mood上的country關(guān)系。對(duì)于后?這種情況,我們還需要在新的country對(duì)象上設(shè)置continent。我們采?和上?為country所做的完全相同的?式來(lái)取回這個(gè)continent對(duì)象:staticfunc?ndOrCreateContinent(forisoCountry:incontext:NSManagedObjectContext)->{guardletiso3166=ISO3166.Continent(country:letpredicate=NSPredicate(format:"%K==%d",letcontinent=?ndOrCreate(in:context,matching:predicate){$0.iso3166Code=$0.updatedAt=}}在上?的例??,我們只從“對(duì)?”?向通過(guò)直接設(shè)置關(guān)系的另?邊上的對(duì)象屬性建?了我們的“對(duì)多關(guān)系。當(dāng)然,你也可以在另?端修改?個(gè)關(guān)系,也就是修改關(guān)系的“對(duì)多?向的對(duì)象。}個(gè)可變版本來(lái)改變關(guān)系,?如,可以添加?個(gè)新的mood對(duì)象:不是mutableSetValue(forKey?法就可以了。系的輔助?法。但是,我們建議直接使?可變的(有序的)set?法。需要決定應(yīng)該如何處理關(guān)聯(lián)的對(duì)象。例如,當(dāng)?個(gè)country對(duì)象被刪除時(shí),CoreData需要更新相應(yīng)continent對(duì)象上的countries關(guān)系來(lái)對(duì)更改做出響應(yīng)。為了實(shí)現(xiàn)這個(gè)?標(biāo),我們?cè)O(shè)置country的continentnullify置空)。這會(huì)導(dǎo)致關(guān)聯(lián)的對(duì)象在我們的例?我們嘗試刪除continent對(duì)象時(shí)就將失?。盒路聪蜿P(guān)系(們),?是由我們開(kāi)發(fā)者向CoreData保證我們已經(jīng)準(zhǔn)備好了更新它們的?定義代我們希望清理不再引?任何mood的country對(duì)象,以及不再引?country的continent對(duì)象。我們可以通過(guò)對(duì)Country類的prepareForDeletion?法進(jìn)?掛鉤(hooking)來(lái)實(shí)現(xiàn)這個(gè)需求://{}}}這個(gè)?在對(duì)象被刪除之前被調(diào)?。在該?法?,我們可以檢查continentcountries關(guān)系是否仍然包含未刪除的country對(duì)象。如果沒(méi)有未刪除的country對(duì)象,我們則會(huì)刪除這個(gè)它插?到導(dǎo)航棧中?于展?mood對(duì)象的viewcontroller之前的位置。這個(gè)tableview將countrycontinent顯?在?個(gè)組合列表?。另外,它還有?個(gè)過(guò)濾選項(xiàng)來(lái)讓列表只顯?continent或者country:相同的通?數(shù)據(jù)源(datasource)類。我們通過(guò)在regiontableviewcontroller的viewDidLoad?法?調(diào)?如下?法來(lái)進(jìn)?設(shè)置://request.fetchBatchSize=20managedObjectContext:managedObjectContext,sectionNameKeyPath:nil,cacheName:nil)delegate:self)} }}的?類,并讓Region遵循LocalizedStringConvertible協(xié)議。但這樣?來(lái),我們就不得不為個(gè)實(shí)現(xiàn)不會(huì)被?到。所以我們把fatalError語(yǔ)句移?Region的localizedDescription屬另?個(gè)選擇是給我們的TableViewDataSource類添加?個(gè)泛型參數(shù),這樣?來(lái)我們可以利? 在本章中,我們?cè)黾恿藘蓚€(gè)新的實(shí)體,Country和Continent,并在它們之間建?了關(guān)系:?有了這些新的實(shí)體和關(guān)系之后,我們更新了我們插?新mood的便捷?法,如果不存在相應(yīng)的country和continent,這個(gè)??動(dòng)創(chuàng)建它們。作為?個(gè)性能優(yōu)化,我們?cè)诨赝巳ナ?較慢的獲取請(qǐng)求之前,?先對(duì)上下??已的對(duì)象進(jìn)?遍歷,來(lái)檢查?個(gè)country或continent是→CoreData可以處理“?對(duì)?”,“?對(duì)多”,以及“多對(duì)多→“→→→使?mutableSet(forKey:)或者mutableOrderedSet(forKey:)存取?法來(lái)修改“對(duì)多”5 數(shù)字具有多種格式:16位、326410的算術(shù)的?進(jìn)制數(shù)等。布爾類型和?期類型也是通過(guò)數(shù)字來(lái)實(shí)現(xiàn)的。布爾值?般被為0或者 較?的數(shù)字(?如在-32,768和+32,767之間),使??個(gè)16位整數(shù)應(yīng)該就夠了,這不在模型編輯器?選擇的數(shù)值類型必須要和你在NSManagedObject?類?對(duì)應(yīng)的屬性所使?的定義成Int16。這?點(diǎn)也同樣適?于浮點(diǎn)類型,即單精度和雙精度屬性必須相應(yīng)地表?為Float和Double。當(dāng)在Objective-C?實(shí)現(xiàn)你的托管對(duì)象?類時(shí),你可以要么使?NSNumber來(lái)表?所有的數(shù)值屬性(整數(shù)和各種?度的浮點(diǎn)值),要么使?恰當(dāng)?shù)臉?biāo)量類型(scalartype)來(lái) ?進(jìn)制數(shù)在Swift和Objective-C?都是?NSDecimalNumber來(lái)表?的。如果你正好需要存儲(chǔ)貨幣值,那么你應(yīng)該仔細(xì)看看這個(gè)選項(xiàng)。NSDecimalNumber可以讓你精確地指定數(shù)字該如時(shí)間以使???當(dāng)前的時(shí)區(qū),來(lái)在UI?展??期。在屬性??進(jìn)制數(shù)據(jù)(即NSData或Data類型是?常簡(jiǎn)單的。它可以被直接(JPEG這樣的數(shù)據(jù)或間接(?如下?會(huì)提到的?定義數(shù)據(jù)類型對(duì)于?進(jìn)制值,CoreData?持所謂的外部 (externalstorage),可以通過(guò)設(shè)置NSAttributeDescription實(shí)例的allow ternalBinaryDataStorage屬性,或者是在Data 在?還者是 成外部?件。底層的可以直接在數(shù)據(jù)庫(kù)??效地不超過(guò)?概100kb的?進(jìn)制數(shù)據(jù)。?般來(lái)說(shuō)這個(gè)選項(xiàng)通常都另外?種?法是只在CoreData??件名,然后你??在磁盤(pán)上管理實(shí)際數(shù)據(jù)的。但你要承擔(dān)確保CoreData和你??的?進(jìn)制的數(shù)據(jù)之間的統(tǒng)?性的全部責(zé)任,這并不總是雜性,以及基于語(yǔ)?不同,對(duì)于正確?為的預(yù)期也會(huì)有所不同,所以這件事情本來(lái)就很。我們有?整章專?講解有關(guān)字符串的?級(jí)。性和臨時(shí)(Transient)屬性的概念。在后?我們討論如何實(shí)現(xiàn)?定義數(shù)據(jù)類型的存取?法(accessormethod)時(shí),我們需要?到這些概念。在NSManagedObject?類?,CoreData會(huì)為表?實(shí)體的屬性動(dòng)態(tài)地實(shí)現(xiàn)setter和getter?法。這就是為什么我們要在Swift?把這些屬性的標(biāo)記為@NSManaged的原因:這會(huì)告訴編譯器CoreData將在運(yùn)?時(shí)提供這些存取?法。這些存取?法處理了所有CoreData相關(guān)的任務(wù),?如從?加載惰值數(shù)據(jù),記錄更改等等。除了那些公開(kāi)的屬性,CoreData也為每個(gè)屬性實(shí)現(xiàn)了所謂的原始屬性。原始屬性使?primitive作為前綴,后?跟著以?寫(xiě)字?開(kāi)頭的屬性的名稱。舉個(gè)例?,對(duì)于date屬性,它的原始屬性是primitiveDate。為了能在我們的?定義類?使?這些屬性,須?相同的之外,你不應(yīng)該直接它們。在下?更詳細(xì)地討論這?點(diǎn)。和正常的(?@NSManaged屬性相?,使?臨時(shí)屬性的優(yōu)勢(shì)是它們可以參與CoreData的變棄掉(下?章會(huì)有關(guān)于惰值化的介紹)。這樣?來(lái),你就不會(huì)遇到內(nèi)存中的屬性和CoreData中的屬性不同步所造成的。那就是Mood實(shí)體的colors屬性,它的類型就是可轉(zhuǎn)換的(Transformable)。遵循NSCoding協(xié)議的數(shù)據(jù)類型都可以直接為可轉(zhuǎn)換的屬性。不過(guò),你也可以指定?個(gè)?定義值轉(zhuǎn)換器(valuetransformer)來(lái)?更?效的格式 某些地?,那么CoreData將?法這種變化。反過(guò)來(lái),這還會(huì)導(dǎo)致不確定?為以及可能造在本節(jié)中,實(shí)現(xiàn)?種更?效 Mood實(shí)體中colors屬性的?法。在第?章?,因?yàn)镹SArray和UIColor都遵循NSCoding協(xié)議,所以我們簡(jiǎn)單地使?了可轉(zhuǎn)換的屬性。因?yàn)檫@些我們可以通過(guò)提供?定義的值轉(zhuǎn)換器來(lái)更?效地這些數(shù)據(jù):將colors數(shù)組為紅綠藍(lán)值alpha通道的值。)第?步是創(chuàng)建兩個(gè)函數(shù),把colors數(shù)組轉(zhuǎn)成Data,以及反過(guò)來(lái)把Data轉(zhuǎn)換成colors數(shù)組。為此,在[UIColor]和Data上增加兩個(gè)計(jì)算屬性。讓我們先來(lái)看看從[UIColor]到Data的count:$0.count)}}}extension{varred:CGFloat=0return[UInt8(red*255),UInt8(green*255),UInt8(blue*}}接下來(lái),我們?Swift的withUnsafeBufferPointer?法把這個(gè)8位?符號(hào)整數(shù)的數(shù)組轉(zhuǎn)換成我們可以?它來(lái)創(chuàng)建?個(gè)Data實(shí)例。guardcount>0&&count%3==0else{returnnil}}}guardletcolor=UIColor(rawData:slice)}}}}否能被三整除。如果數(shù)據(jù)是有效的,我們創(chuàng)建類型為[Uint8]的正確??的rgbValues數(shù)組來(lái)?法,把數(shù)據(jù)從Data的buffer?到我們創(chuàng)建的數(shù)組buffer中去。剩下的就很容易了,我們將數(shù)組按照每三個(gè)?組進(jìn)?切分,然后使?如下的UIColorconvenience初始化?法?成UIColor對(duì)象:letred=CGFloat(rawData[0])/255}}_=self.}?leprivatestatic registerOnce:()}

returndatafunccreateMoodyContainer(completion:{//}從現(xiàn)在開(kāi)始,我們的color數(shù)據(jù)將被成?常緊湊和?效的?進(jìn)制數(shù)據(jù):?,我們使?可轉(zhuǎn)換屬性并結(jié)合?定義值轉(zhuǎn)換器,實(shí)現(xiàn)了??的colors數(shù)組的?式。持久化,然后添加?個(gè)臨時(shí)(即?持久化)的屬性,并為這個(gè)臨時(shí)屬性實(shí)現(xiàn)我們??的、公際被需要的時(shí)候是合理的。對(duì)于我們的colors數(shù)組的場(chǎng)景,使?可轉(zhuǎn)換屬性是?常合適再給Mood類添加相應(yīng)的屬性:colors屬性的類型由可轉(zhuǎn)換(transformable)改為未定義(unde?ned),同時(shí)我們?cè)贒ataModelInspector?勾上“Transient”的復(fù)選框來(lái)將其設(shè)定為臨時(shí)屬性。臨時(shí)屬性不會(huì)被保存在為我們的?定義的臨時(shí)屬性colors實(shí)現(xiàn)getter和setter之前,我們?先要討論的是原始屬性。托管對(duì)象?類的每個(gè)@NSManaged屬性都有?個(gè)底層的原始屬性,這些屬性的存取?法是由CoreData動(dòng)態(tài)?成的。原始的存取?法讓你可以到托管對(duì)象的內(nèi)部。為了不讓編譯器報(bào)錯(cuò),我們需要把原始屬性成這樣:和colorStorage?樣,primitiveColors屬性也被 varc=primitiveColorsifc==nil{primitiveColors=c}}setprimitiveColors=newValuecolorStorage=newValue.moodData}}這段代碼?重要的?點(diǎn)是,我們?cè)趃etter?使?willAccessValue(forKey:)和didAccessValue(forKey:)?法,在setter?使?willChangeValue(forKey:)和Data在幕后執(zhí)?通常的整理?作。這個(gè)?定義的getter只會(huì)在你第?次屬性的時(shí)候執(zhí)?從?進(jìn)制數(shù)據(jù)到colors數(shù)組的轉(zhuǎn)化。另???,setter在你給colors屬性設(shè)置新值時(shí)會(huì)執(zhí)?colors數(shù)組到?進(jìn)制數(shù)據(jù)的轉(zhuǎn)換。如果這個(gè)開(kāi)銷太昂貴,還有另?種做法:你可以把轉(zhuǎn)換這?步從setter?移動(dòng)到托管對(duì)象的willSave?法?,這樣只會(huì)在每次保存的時(shí)候執(zhí)?這?步。enumMessageType:Int16casetext=caseimage=}//}getguardletval=MessageType(rawValue:else{fatalError("invalidenumvalue")}returnval}willChangeValue(forKey:Message.typeKey)didChangeValue(forKey:Message.typeKey)}}} 數(shù)值屬性的原始屬性的類型總是可以?NSNumber來(lái)表?。另?個(gè)可以在CoreData屬性上使??定義setter所有數(shù)據(jù)類型的屬性都可以設(shè)置默認(rèn)值。CoreData會(huì)在?個(gè)對(duì)象?到上下?時(shí)?動(dòng)將屬性設(shè)置為這個(gè)值。確保對(duì)象在開(kāi)始時(shí)具有合法的值會(huì)?常有?,如果再能和Swift?的?默認(rèn)情況下,被CoreData托管的對(duì)象的所有屬性都是可選值:它們要么有值,要么是nil。但是你還是可以把?個(gè)CoreData的屬性設(shè)置成不可選的,然后在你的托管對(duì)象?類?為相應(yīng)的屬性使??可選值的類型。在使?謂詞的時(shí)候,確保值不是nil尤其重要,因?yàn)閚il在這些情況下具有特殊的意義。在謂詞這?章?會(huì)深?探討這??的內(nèi)容。你還可以在運(yùn)?時(shí)設(shè)置默認(rèn)值。Mood實(shí)體的date屬性就是這??很好的?個(gè)例?。每次我們類的awakeFromInsert?法://primitiveDate=Date()}@NSManaged?leprivatevarprimitiveDate://}awakeFromInsert?法在對(duì)象的?命周期?只會(huì)被調(diào)??次,正如其名,也就是對(duì)象第?次在本章中,我們探討了CoreData的默認(rèn)數(shù)據(jù)類型和其他?定義的數(shù)據(jù)類型的可能性。對(duì)→→→)6協(xié)作的。我們還將看看如何利?CoreData提供的?級(jí)選項(xiàng)來(lái)獲得對(duì)整個(gè)流程的控制。接能。為了能讓你處理巨?的數(shù)據(jù)集,CoreData其實(shí)做了很多繁重的?作。在簡(jiǎn)單的使?場(chǎng)景下,你并不需要知道這些也能使?CoreData。但是如果能理解Core)貫穿整章,假設(shè)我們都會(huì)使?默認(rèn)的持久化獲取請(qǐng)求(FetchRequests)是最顯?易?的從CoreData?獲取對(duì)象的?式。讓我們來(lái)看看,letrequest=NSFetchRequest<Mood>(entityName:上下?通過(guò)調(diào)?execute(_request:withcontext:)?法把獲取請(qǐng)求轉(zhuǎn)交給它的持久化協(xié)調(diào)器。請(qǐng)注意這?上下?將??作為第?個(gè)參數(shù)傳?它在后?會(huì)被使?到。持久化協(xié)調(diào)器通過(guò)調(diào)?每個(gè)上的execute(_request:withcontext:)?法將獲取請(qǐng)求轉(zhuǎn)發(fā)給所有的持久化們(假如你有多個(gè)的話)。再次注意:發(fā)起獲取請(qǐng)求的上下?被傳遞給了持久化。持久化把獲取請(qǐng)求轉(zhuǎn)換成?個(gè)SQL語(yǔ)句,并把這個(gè)SQL語(yǔ)句發(fā)送給 細(xì)節(jié)具體可以參考?章)。這些?同時(shí)包含了對(duì)象的ID(ObjectID)和屬性的數(shù)據(jù)(因?yàn)楂@取請(qǐng)求的includesPropertyValues選項(xiàng)默認(rèn)值是true)。對(duì)象的ID是?記錄的唯?標(biāo)識(shí)—事實(shí)上,它們是持久化的ID、表的ID以及表返回的原始數(shù)據(jù)是由數(shù)字、字符串和?進(jìn)制?對(duì)象(BLOB,BinaryLargeObjects)這樣的簡(jiǎn)單的數(shù)據(jù)類型組成的。它被在持久化的?緩存(rowcache)?,?起的還有對(duì)象ID和緩存條?最后更新的時(shí)間戳。只要在上下??存在某個(gè)特定對(duì)象ID的持久化把它從接收到的對(duì)象ID實(shí)例化為托管對(duì)象,并把這些對(duì)象返回給協(xié)調(diào)器。為了實(shí)現(xiàn)這個(gè)?的,需要調(diào)?上下?的object(with:)?法,因?yàn)橥泄軐?duì)獲取請(qǐng)求的默認(rèn)?為是返回托管對(duì)象(其實(shí)還可以是其他的結(jié)果類型(resulttypes),不對(duì)象。它們承諾會(huì)在你需要的時(shí)候去加載數(shù)據(jù)(后?會(huì)介紹關(guān)于惰值的內(nèi)容)。持久化協(xié)調(diào)器把它從持久化因?yàn)楂@取請(qǐng)求的includesPendingChanges屬性默認(rèn)值是true,在返回獲取請(qǐng)求的結(jié)果在iOS10/macOS10.12之前的版本?,持久化協(xié)調(diào)器也會(huì)被阻塞。 execute(_ ??(????????(??????

???在這個(gè)過(guò)程中,最重要的部分是CoreData的惰值化和唯?性機(jī)制。惰值允許你?需在內(nèi)存中你可以通過(guò)設(shè)置returnsObjectsAsFaults屬性來(lái)控制獲取請(qǐng)求是返回惰值,還是返回完全實(shí)體化的對(duì)象,它默認(rèn)是true。將其設(shè)置為false會(huì)讓CoreData?實(shí)際數(shù)據(jù)預(yù)先填充返回的對(duì)象。CoreData的屬性存取?法(accessor)內(nèi)部會(huì)調(diào)?willAccessValue(forKey:),這個(gè)?CoreData會(huì)在運(yùn)?時(shí)為標(biāo)記為@NSManaged的屬性實(shí)現(xiàn)屬性存取?法,所以它可以在和寫(xiě)?屬性值時(shí)注???的?為,?如填充?個(gè)惰值。這也是CoreData可以感據(jù)。接下來(lái),上下?會(huì)向它的持久化協(xié)調(diào)器請(qǐng)求這些數(shù)據(jù)。持久化協(xié)調(diào)器通過(guò)調(diào)?newValuesForObject(withobjectID:withcontext:)?法向持久化請(qǐng)求與對(duì)象ID相關(guān)聯(lián)的數(shù)據(jù)。持久化在?緩存?查找這個(gè)對(duì)象ID的數(shù)據(jù)。如果緩存的數(shù)據(jù)還沒(méi)有失效,我們會(huì)緩存數(shù)據(jù)是否失效是由上下?的stalenessInterval屬性來(lái)決定的。默認(rèn)情況下,它被設(shè)置為0,這意味著緩存的數(shù)據(jù)不會(huì)失效,如果存在緩存,持久化協(xié)調(diào)器總會(huì)返回緩存?的數(shù)據(jù)。如果你設(shè)置stalenessInterval屬性為正值,那么只有當(dāng)緩存數(shù)據(jù)的最后更新時(shí)間?于失效的時(shí)間間隔(單位是秒時(shí)緩存數(shù)據(jù)才會(huì)被使?。如果我們沒(méi)有命中緩存或緩存的數(shù)據(jù)已失效,持久化?會(huì)成相應(yīng)的SQL語(yǔ)句來(lái)從?檢索數(shù)據(jù)。持久化會(huì)執(zhí)?這個(gè)SQL語(yǔ)句并將數(shù)據(jù)返回給協(xié)調(diào)器。新獲取的數(shù)據(jù)也會(huì)被在?緩存?。象,或者?CoreData的說(shuō)法是,惰值已被填充。在這?步,?緩存?的原始數(shù)據(jù)被并轉(zhuǎn)化成正確的托管對(duì)象的數(shù)據(jù)類型。例如,可轉(zhuǎn)換屬性會(huì)從它們的Data表現(xiàn)形式轉(zhuǎn)換成?向??的類型。此外,上下?將保留這些數(shù)據(jù)的快照(snapshot),?的是在你之后保存這些數(shù)據(jù)時(shí)能夠檢測(cè)和解決。你可以在下?章更改和保存數(shù)據(jù)?閱讀到關(guān)于這?點(diǎn)的內(nèi)容。??(??????newValuesForObject(withobjectId:with?正如你所看到的,從?緩存?填充惰值是?種相對(duì)廉價(jià)的操作因?yàn)檫@?切都發(fā)?在內(nèi)存?。執(zhí)?普通的獲取請(qǐng)求后(即其中的returnsObjectsAsFaults和includesPropertyValues相當(dāng)廉價(jià)的這是在較?內(nèi)存占?和更快填充返回的惰值之間的?個(gè)權(quán)衡。但是,通過(guò)設(shè)置includesPropertyValues屬性為false,你可以改變特定獲取請(qǐng)求的默認(rèn)?為,防?它從數(shù)據(jù)庫(kù)?加載除了對(duì)象ID之外的任何屬性值。只獲取對(duì)象ID本?可以?常有?。例如,CoreData的批量獲取的機(jī)制就是利?了這?點(diǎn)。我們將在下?更詳細(xì)地討論這個(gè)特別的如果將includesropertyValues設(shè)置為alse值,會(huì)導(dǎo)致另?次到te的往返,除?這部分?jǐn)?shù)據(jù)已經(jīng)通過(guò)另?種?式取回了。這種獲取請(qǐng)個(gè)對(duì)象調(diào)?上下?的refresh(_object:mergeChanges:)?法。這個(gè)?法的第?個(gè)參數(shù),這?乎總是你想要做的(refreshAllObjects(?法的做法也是如此)。獲取請(qǐng)求有個(gè)叫shouldRefreshRefetchedObjects的選項(xiàng),它會(huì)導(dǎo)致上下??動(dòng)刷新所有已有持久化?值的便捷?法。返回NSManagedObjectID實(shí)例的數(shù)組?不是通常的托管對(duì)象。但是請(qǐng)注意,這樣的獲取請(qǐng)求那么你還是需要把獲取請(qǐng)求的includesPropertyValues屬性設(shè)為false。謂詞和排序描述符,但是指定結(jié)果類型為對(duì)象ID,并設(shè)置includesPropertyValues為false。將所需對(duì)象的ID傳遞到?個(gè)selfIN%@的謂詞?增量地獲取數(shù)據(jù)。這實(shí)際上就是Core)結(jié)果類型.countResultType,在概念上等同于使?count(forrequest:)execute(_request:)?法。任何時(shí)候,如果你只需要知道結(jié)果的數(shù)量,請(qǐng)務(wù)必使?這個(gè)結(jié)果類最后,還有.ditioaryRetType。雖然這個(gè)結(jié)果類型有點(diǎn)復(fù)雜,但是它的功能?常強(qiáng)?。它的基本思路是:?個(gè)使?字典結(jié)果類型的獲取請(qǐng)求將不再返回?個(gè)托管對(duì)象的數(shù)組來(lái)表?你請(qǐng)求的數(shù)據(jù),?是返回?個(gè)包含原始數(shù)據(jù)的字典的數(shù)組。這種?為讓?些有趣的使?場(chǎng)景成為可?先,你可以通過(guò)設(shè)置propertiesToFetch來(lái)指定只取回實(shí)體的某些屬性。CoreData之后只 作。假設(shè)我們有個(gè)Employee實(shí)體,它有?個(gè)type屬性和?個(gè)salary屬性。如果我們想知道員?(employee)的平均薪資(salary),并希望按員?的類型來(lái)分組。我們可以使?字典結(jié)果類letrequest=NSFetchRequest<NSManagedObject>(entityName:"Employee")request.resultType=.dictionaryResultTypeletsalaryExp=N salaryExp.expressionResultType=.doubleAttributeTypesalaryExp.expression=N pression(forFunction:"average:",arguments:[N pression(forKeyPath:"salary")])salaryE="avgSalary"pertiesToGroupBy=["type"]pertiesToFetch=["type",salaryExp]我們使?的是兩個(gè)當(dāng)NSFetchRequestpropertiesToGroup

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論