Core Data - 數(shù)據(jù)持久化

前言

廣而告之 這里有其他人對(duì)CoreData的理解溪椎,下邊是對(duì)部分內(nèi)容進(jìn)行的引用恬口。

CoreData是Apple官方為iOS提供的一個(gè)數(shù)據(jù)持久化方案祖能,其本質(zhì)是一個(gè)通過封裝底層數(shù)據(jù)操作,讓程序員以面向?qū)ο蟮姆绞酱鎯?chǔ)和管理數(shù)據(jù)的ORM框架(Object-Relational Mapping:對(duì)象-關(guān)系映射雁芙,簡稱ORM)钞螟。雖然底層支持SQLite、二進(jìn)制數(shù)據(jù)裂明、xml等多種文件存儲(chǔ)太援,但是主要還是用來操作SQLite數(shù)據(jù)庫。

這里不對(duì)上文進(jìn)行評(píng)價(jià)仙蛉,接下來介紹對(duì)Core Data的理解碱蒙。

嗯,說一句吧哀墓,Core data 是Object-oriented database。

Core Data

Manage object graphs and object lifecycle, including persistence.

不管是面向?qū)ο笠埠煤罄祝嫦蜻^程也好吠各,最終處理的還是數(shù)據(jù)贾漏,所以,Core Data 也就是為了解決我們抽象出來的對(duì)象和數(shù)據(jù)之間的關(guān)系梳码。

使用Core Data 第一個(gè)好處就是 它可以大大減少我們的工作量?不需要懂SQL?伍掀,就像我們使用xib、storyboard一樣,簡單的事情做多了就沒有意思了瘩例,至于懂不懂SQL甸各,因人而異,我覺得把自己那些東西學(xué)明白了就好了聘惦,不然儒恋,今天別人給你安利一個(gè)這,看個(gè)這禀酱,明天弄個(gè)那牧嫉,沒意思,過幾天回過頭曹洽,看一眼自己的工程送淆,一團(tuán)糟,蘋果一天不倒閉砖第,你就有飯吃环凿,別焦慮。

1創(chuàng)建Core Data羽杰、 存儲(chǔ)和讀取

如果創(chuàng)建工程的時(shí)候??勾選了Core Data到推。

如果忘記了勾選,可以創(chuàng)建一個(gè)颜骤,創(chuàng)建時(shí)候需要注意捣卤,創(chuàng)建的是Data Model董朝,不是Mapping Model。

創(chuàng)建成功后祟绊,項(xiàng)目中會(huì)出現(xiàn) .xcdatamodeld 文件哥捕,而且還會(huì)在AppDelegate.swift中出現(xiàn)下邊這些代碼。

funcapplicationWillTerminate(_application:UIApplication) {

? ? ? ? // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.

? ? ? ? // Saves changes in the application's managed object context before the application terminates.

? ? ? ? self.saveContext()

? ? }

? ? // MARK: - Core Data stack

? ? lazyvarpersistentContainer:NSPersistentContainer= {

? ? ? ? /*

?? ? ? ? The persistent container for the application. This implementation

?? ? ? ? creates and returns a container, having loaded the store for the

?? ? ? ? application to it. This property is optional since there are legitimate

?? ? ? ? error conditions that could cause the creation of the store to fail.

? ? ? ? */

? ? ? ? letcontainer =NSPersistentContainer(name:"codedata")

? ? ? ? container.loadPersistentStores(completionHandler: { (storeDescription, error)in

? ? ? ? ? ? ifleterror = errorasNSError? {

? ? ? ? ? ? ? ? // Replace this implementation with code to handle the error appropriately.

? ? ? ? ? ? ? ? // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.


? ? ? ? ? ? ? ? /*

?? ? ? ? ? ? ? ? Typical reasons for an error here include:

?? ? ? ? ? ? ? ? * The parent directory does not exist, cannot be created, or disallows writing.

?? ? ? ? ? ? ? ? * The persistent store is not accessible, due to permissions or data protection when the device is locked.

?? ? ? ? ? ? ? ? * The device is out of space.

?? ? ? ? ? ? ? ? * The store could not be migrated to the current model version.

?? ? ? ? ? ? ? ? Check the error message to determine what the actual problem was.

?? ? ? ? ? ? ? ? */

? ? ? ? ? ? ? ? fatalError("Unresolved error \(error), \(error.userInfo)")

? ? ? ? ? ? }

? ? ? ? })

? ? ? ? returncontainer

? ? }()

? ? // MARK: - Core Data Saving support

? ? funcsaveContext () {

? ? ? ? letcontext =persistentContainer.viewContext

? ? ? ? ifcontext.hasChanges{

? ? ? ? ? ? do{

? ? ? ? ? ? ? ? trycontext.save()

? ? ? ? ? ? }catch{

? ? ? ? ? ? ? ? // Replace this implementation with code to handle the error appropriately.

? ? ? ? ? ? ? ? // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

? ? ? ? ? ? ? ? letnserror = errorasNSError

? ? ? ? ? ? ? ? fatalError("Unresolved error \(nserror), \(nserror.userInfo)")

? ? ? ? ? ? }

? ? ? ? }

? ? }

如果是New File創(chuàng)建的鸽捻,那么你就需要這些代碼泽腮,需要將NSPersistentContainer中的name更改為你所New的File name衣赶。如果你程序訪問數(shù)據(jù)庫府瞄,或怎么怎么地,出現(xiàn)問題鲸郊,哎货邓,這樣的事情,試著在applicationWillTerminate中調(diào)用一下saceContext()方法职辨。

至于為什么調(diào)用該方法呢戈二?

保存你的數(shù)據(jù)庫,到disk中腾供,disk是啥鲜滩?嗯。


如果你不想使用?NSPersistenContainer?方法對(duì)數(shù)據(jù)庫進(jìn)行保存,或者你想將數(shù)據(jù)庫保存到其他位置闷游,可以嘗試使用 ?NSManagedObjectModel +?NSPersistentStoreCoordinator 的形式進(jìn)行存儲(chǔ)操作贴汪。

guard let modelURL = Bundle.main.url(forResource:"DataModel", withExtension:"momd")else{

????fatalError("failed to find data model")

}

guard let mom = NSManagedObjectModel(contentsOf: url)else{

????fatalError("Failed to create model from file:\(url)")

}

先拿出你的model 然后 通過?NSPersistentStoreCoordinator 保存

let psc =NSPersistentStoreCoordinator(managedObjectModel: mom)

let dirURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last ????

?let fileURL = URL(string: "DataModel.sql", relativeTo: dirURL)

do {

????try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: fileURL, options: nil)

} catch {

????fatalError("Error configuring persistent store: \(error)")

}

最后還需要這扳埂,你存儲(chǔ)/讀取的大部分時(shí)間都是和?NSManagerObjectContext 打交道的

?let context = persistentContainer.viewContext

let moc =NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) ????????????

moc.persistentStoreCoordinator = psc

經(jīng)驗(yàn)之談

這里可以寫 static 方法阳懂,調(diào)用起來也方便,多個(gè)數(shù)據(jù)庫柜思,就創(chuàng)建多個(gè)(但不遵循我不寫重復(fù)代碼的原則呀O镌铩!)陨享。

static var persistentContainer:NSPersistentContainer{

? ? ? ? ? ?return (UIApplication.shared.delegate as! AppDelegate).persistentContainer

}

static var coreDataViewContext:NSManagedObjectContext { ?

?????????return AppDelegate.persistentContainer.viewContext?

}

2 使用Core Data - ugly

使用core data 存儲(chǔ)的時(shí)候 需要借助 剛剛那個(gè)viewContext抛姑,just like this->

let context = AppDelegate.coreDataViewContext

letpeoperObject:NSManagedObject=NSEntityDescription.insertNewObject(forEntityName:"People", into: context)

peoperObject.setValue("zhanshan", forKey:"name")

讀取一樣的道理

peoperObject.value(forKey:"name")

what艳狐? 這NM還不如不用呢! (一看見別人寫key-value喷斋,我就不舒服)

3 使用Core Data ?- 存儲(chǔ)和讀取

可以通過修改xcdatamodeld文件中ENTITLES中的Codegen來減少工作量蒜茴。

1修改Codegen - 為Category/Extension粉私。

2新建一個(gè)ENTITLES 繼承NSManagedObject

然后你可以嘗試自己new一個(gè)你新建的對(duì)象,你會(huì)發(fā)現(xiàn)抄肖,你沒有寫任何屬性窖杀,它就存在你core data 中對(duì)應(yīng)ENTITIES的屬性。

其實(shí)當(dāng)你新建的時(shí)候管毙,core data 就給你已經(jīng)寫好了桌硫。你創(chuàng)建的ENTITIES 為people 那么它就會(huì)給你寫好extension铆隘,有什么Attributes,就同樣創(chuàng)建對(duì)應(yīng)的屬性供你使用掏湾。

//extension People {

//

//? ? @nonobjc public class func fetchRequest() -> NSFetchRequest {

//? ? ? ? return NSFetchRequest(entityName: "People")

//? ? }

//

//? ? @NSManaged public var age: Int16

//? ? @NSManaged public var birthday: Date?

//? ? @NSManaged public var create: Date?

//? ? @NSManaged public var name: String?

//? ? @NSManaged public var sex: String?

//? ? @NSManaged public var adopt: NSSet?

//

//}

如果ENTITIES之間存在relationship,那么ENTITIES即存在對(duì)應(yīng)關(guān)系(1對(duì)1恒界,1對(duì)多)砚嘴,例如:people和dog存在relationship际长,且關(guān)系為To Many,那么你通過查詢 得到了具體某個(gè)people時(shí)虾宇,操作dog需要調(diào)用people.managedObjectContext來對(duì)dog進(jìn)行操作如绸。

其中ENTITIES實(shí)例化需要?NSManagedObjectContext

? @available(iOS 10.0, *)

? ? public convenience init(context moc: NSManagedObjectContext)

通過重寫 ENTITIES 中?NSManagedObjectContext 的方法來實(shí)現(xiàn)一些操作,比如?prepareForDeletion搪泳,ENTITIES在被刪除的時(shí)候會(huì)調(diào)用這個(gè)方法扼脐。

3 使用Core Data ?- 查詢

查詢某個(gè)ENTITIES下的數(shù)據(jù)需要使用對(duì)應(yīng)ENTITIES下的?fetchRequest() , 例如 People.fetchRequest().

上一步返回NSFetchRequest瓦侮。

通過配置request的predicate,來進(jìn)行匹配查詢方妖,多個(gè)匹配規(guī)則使用NSCompoundPredicate罚攀,NSPredicate在使用的時(shí)候需要注意。

通過配置sortDescriptors,來對(duì)數(shù)據(jù)結(jié)果進(jìn)行排序,可以使用多個(gè)sortDescriptor是己。NSSortDescriptor使用的時(shí)候需要注意任柜,如果使用自定義排序算法沛厨,需要給對(duì)應(yīng)屬性寫擴(kuò)展方法逆皮,例如参袱,NSNumber的屬性,就對(duì)應(yīng)寫好它的擴(kuò)展排序方法剿牺,并通過返回NSComparisonResult(其中包含返回結(jié)果)來進(jìn)行排序晒来。

通過context對(duì)象調(diào)用fatch 進(jìn)行查詢郑现,例如let info =try? context.fetch(request)。

查詢返回結(jié)果?NSFetchRequest<NSFetchRequestResult>攒读,這里需要注意列牺,你所創(chuàng)建的ENTITIES都默認(rèn)遵守NSManagedObject : NSFetchRequestResult協(xié)議 瞎领,所以對(duì)于返回結(jié)果可以如下。

? ? for item in result ?? [] {

? ? ? ? ? ? let p:People = item

? ? ? ? ? ? print(p.name??"ss")

? ? ? ? }

應(yīng)對(duì)面試震放,到這里就可以了驼修。

如果你是工作中使用,那么接下來就是經(jīng)驗(yàn)之談了墨礁。

網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)本地化

是否存在一種不需要賦值恩静,數(shù)據(jù)主動(dòng)映射的方法,這樣我們可以直接寫網(wǎng)絡(luò)請(qǐng)求,然后什么也不用管邑飒,數(shù)據(jù)就直接映射到model里级乐,還進(jìn)行了本地化风科?用的時(shí)候直接從Core Data取丐重?

是的扮惦,有

基本上的流程就是 Data ->JSONModel ->NSManagedObject-> Core Data.

你需要將Core Data 中的ENTITIES,relationship使用的非常熟練浊仆,是非常熟練豫领。

如果上一步做的沒有問題等恐,那么你工程中會(huì)出現(xiàn)好多沒有屬性和方法的NSManagedObject,你的Core Data 界面也非常有條理囱稽,ENTITIES之間關(guān)聯(lián)的線也連好了二跋。

下一步?

是否存在JSONModel-NSManagedObject有一種關(guān)聯(lián),那么問題基本就解決了吞获。

是的谚鄙,有

github有一個(gè)庫Groot闷营,真好。

Groot provides a simple way of serializing Core Data object graphs from or into JSON.

It uses?annotations?in the Core Data model to perform the serialization and provides the following features:

Attribute and relationship mapping to JSON key paths.

Value transformation using named?NSValueTransformer?objects.

Object graph preservation.

Support for entity inheritance

簡單介紹一下蚊荣,這個(gè)庫能夠完成JSONModel-NSManagedObject互相轉(zhuǎn)換莫杈,??。

下載完庫媳叨,你會(huì)發(fā)現(xiàn)关顷,百度里好像沒有幾個(gè)人對(duì)這個(gè)庫進(jìn)行解釋。

有兩種人痘番,

1.如果你是按照(AppDelegate.persistentContainer.viewContext?)平痰,那么你就不需要去了解它的方法介紹汞舱。

2.如果你是按照(let moc =NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) ),那么你可能會(huì)做很多無用功宗雇,你需要讀代碼特別熟練昂芜,你去下載源碼Demo多看看也就會(huì)了。

不用問赔蒲,我肯定用的是第一種泌神。

如果你拿到了Data數(shù)據(jù),可以直接調(diào)用?objects(fromJSONData: data, inContext:NSManagedObjectContext).它就可以給你返回舞虱。

如果你處理的JSONObject欢际,那么你需要使用它的另外兩個(gè)方法。

好了幼苛,??。

你會(huì)發(fā)現(xiàn)能正常存儲(chǔ)焕刮,讀取舶沿,但是ENTITIES中的數(shù)據(jù)都是nil。

接下來你就需要注意了配并,你需要到Core Data 界面配置JSONKeyPath括荡。

每一個(gè)Attributes都配置,注意 溉旋,是添加Attributes中User Info 畸冲,“+” 加上JSONKeyPath,value為你網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)中的key。

每一個(gè)relationship都需要配置邑闲,配置原則和Attributes一樣算行。

非常完美,不用壘代碼真好苫耸。

最后州邢,

try context.save()

我希望你在操作過Core Data數(shù)據(jù)后,能夠優(yōu)美的調(diào)用save方法褪子。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末量淌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子嫌褪,更是在濱河造成了極大的恐慌呀枢,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件笼痛,死亡現(xiàn)場(chǎng)離奇詭異裙秋,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)晃痴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門残吩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人倘核,你說我怎么就攤上這事泣侮。” “怎么了紧唱?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵活尊,是天一觀的道長。 經(jīng)常有香客問我漏益,道長蛹锰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任绰疤,我火速辦了婚禮铜犬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘轻庆。我一直安慰自己癣猾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布余爆。 她就那樣靜靜地躺著纷宇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蛾方。 梳的紋絲不亂的頭發(fā)上像捶,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天上陕,我揣著相機(jī)與錄音,去河邊找鬼拓春。 笑死释簿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的硼莽。 我是一名探鬼主播辕万,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼沉删!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起醉途,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤矾瑰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后隘擎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體殴穴,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年货葬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了采幌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡震桶,死狀恐怖休傍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蹲姐,我是刑警寧澤磨取,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站柴墩,受9級(jí)特大地震影響忙厌,放射性物質(zhì)發(fā)生泄漏江咳。R本人自食惡果不足惜逢净,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望歼指。 院中可真熱鬧爹土,春花似錦、人聲如沸东臀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惰赋。三九已至宰掉,卻和暖如春呵哨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背轨奄。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國打工孟害, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挪拟。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓挨务,卻偏偏與公主長得像,于是被迫代替她去往敵國和親玉组。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谎柄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

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