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

前言

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

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

這里不對上文進(jìn)行評價,接下來介紹對Core Data的理解。

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

Core Data

Manage object graphs and object lifecycle, including persistence.

不管是面向?qū)ο笠埠?,面向過程也好,最終處理的還是數(shù)據(jù),所以,Core Data 也就是為了解決我們抽象出來的對象和數(shù)據(jù)之間的關(guān)系。

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

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

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

如果忘記了勾選,可以創(chuàng)建一個,創(chuàng)建時候需要注意,創(chuàng)建的是Data Model,不是Mapping Model。

創(chuàng)建成功后,項目中會出現(xiàn) .xcdatamodeld 文件,而且還會在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?方法對數(shù)據(jù)庫進(jìn)行保存,或者你想將數(shù)據(jù)庫保存到其他位置,可以嘗試使用 ?NSManagedObjectModel +?NSPersistentStoreCoordinator 的形式進(jìn)行存儲操作。

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)")

}

最后還需要這,你存儲/讀取的大部分時間都是和?NSManagerObjectContext 打交道的

?let context = persistentContainer.viewContext

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

moc.persistentStoreCoordinator = psc

經(jīng)驗之談

這里可以寫 static 方法,調(diào)用起來也方便,多個數(shù)據(jù)庫,就創(chuàng)建多個(但不遵循我不寫重復(fù)代碼的原則呀?。。?。

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 存儲的時候 需要借助 剛剛那個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 ?- 存儲和讀取

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

1修改Codegen - 為Category/Extension。

2新建一個ENTITLES 繼承NSManagedObject

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

其實當(dāng)你新建的時候,core data 就給你已經(jīng)寫好了。你創(chuàng)建的ENTITIES 為people 那么它就會給你寫好extension,有什么Attributes,就同樣創(chuàng)建對應(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即存在對應(yīng)關(guān)系(1對1,1對多),例如:people和dog存在relationship,且關(guān)系為To Many,那么你通過查詢 得到了具體某個people時,操作dog需要調(diào)用people.managedObjectContext來對dog進(jìn)行操作。

其中ENTITIES實例化需要?NSManagedObjectContext

? @available(iOS 10.0, *)

? ? public convenience init(context moc: NSManagedObjectContext)

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

3 使用Core Data ?- 查詢

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

上一步返回NSFetchRequest。

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

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

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

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

? ? for item in result ?? [] {

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

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

? ? ? ? }

應(yīng)對面試,到這里就可以了。

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

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

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

是的,有

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

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

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

下一步?

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

是的,有

github有一個庫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

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

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

有兩種人,

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

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

不用問,我肯定用的是第一種。

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

如果你處理的JSONObject,那么你需要使用它的另外兩個方法。

好了,??。

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

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

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

每一個relationship都需要配置,配置原則和Attributes一樣。

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

最后,

try context.save()

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容