前言
廣而告之 這里有其他人對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方法。