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

下載本文檔

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

文檔簡介

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

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

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

溫馨提示

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

評論

0/150

提交評論