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