前言
广而告之 这里有其他人对CoreData的理解,下边是对部分内容进行的引用。
CoreData是Apple官方为iOS提供的一个数据持久化方案,其本质是一个通过封装底层数据操作,让程序员以面向对象的方式存储和管理数据的ORM框架(Object-Relational Mapping:对象-关系映射,简称ORM)。虽然底层支持SQLite、二进制数据、xml等多种文件存储,但是主要还是用来操作SQLite数据库。
这里不对上文进行评价,接下来介绍对Core Data的理解。
嗯,说一句吧,Core data 是Object-oriented database。
Core Data
Manage object graphs and object lifecycle, including persistence.
不管是面向对象也好,面向过程也好,最终处理的还是数据,所以,Core Data 也就是为了解决我们抽象出来的对象和数据之间的关系。
使用Core Data 第一个好处就是 它可以大大减少我们的工作量 不需要懂SQL ,就像我们使用xib、storyboard一样,简单的事情做多了就没有意思了,至于懂不懂SQL,因人而异,我觉得把自己那些东西学明白了就好了,不然,今天别人给你安利一个这,看个这,明天弄个那,没意思,过几天回过头,看一眼自己的工程,一团糟,苹果一天不倒闭,你就有饭吃,别焦虑。
1创建Core Data、 存储和读取
如果创建工程的时候☑️勾选了Core Data。
如果忘记了勾选,可以创建一个,创建时候需要注意,创建的是Data Model,不是Mapping Model。
创建成功后,项目中会出现 .xcdatamodeld 文件,而且还会在AppDelegate.swift中出现下边这些代码。
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创建的,那么你就需要这些代码,需要将NSPersistentContainer中的name更改为你所New的File name。如果你程序访问数据库,或怎么怎么地,出现问题,哎,这样的事情,试着在applicationWillTerminate中调用一下saceContext()方法。
至于为什么调用该方法呢?
保存你的数据库,到disk中,disk是啥?嗯。
如果你不想使用 NSPersistenContainer 方法对数据库进行保存,或者你想将数据库保存到其他位置,可以尝试使用 NSManagedObjectModel + NSPersistentStoreCoordinator 的形式进行存储操作。
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
经验之谈
这里可以写 static 方法,调用起来也方便,多个数据库,就创建多个(但不遵循我不写重复代码的原则呀!!)。
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一个你新建的对象,你会发现,你没有写任何属性,它就存在你core data 中对应ENTITIES的属性。
其实当你新建的时候,core data 就给你已经写好了。你创建的ENTITIES 为people 那么它就会给你写好extension,有什么Attributes,就同样创建对应的属性供你使用。
//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即存在对应关系(1对1,1对多),例如:people和dog存在relationship,且关系为To Many,那么你通过查询 得到了具体某个people时,操作dog需要调用people.managedObjectContext来对dog进行操作。
其中ENTITIES实例化需要 NSManagedObjectContext
@available(iOS 10.0, *)
public convenience init(context moc: NSManagedObjectContext)
通过重写 ENTITIES 中 NSManagedObjectContext 的方法来实现一些操作,比如 prepareForDeletion,ENTITIES在被删除的时候会调用这个方法。
3 使用Core Data - 查询
查询某个ENTITIES下的数据需要使用对应ENTITIES下的 fetchRequest() , 例如 People.fetchRequest().
上一步返回NSFetchRequest。
通过配置request的predicate,来进行匹配查询,多个匹配规则使用NSCompoundPredicate,NSPredicate在使用的时候需要注意。
通过配置sortDescriptors,来对数据结果进行排序,可以使用多个sortDescriptor。NSSortDescriptor使用的时候需要注意,如果使用自定义排序算法,需要给对应属性写扩展方法,例如,NSNumber的属性,就对应写好它的扩展排序方法,并通过返回NSComparisonResult(其中包含返回结果)来进行排序。
通过context对象调用fatch 进行查询,例如let info =try? context.fetch(request)。
查询返回结果 NSFetchRequest
for item in result ?? [] {
let p:People = item
print(p.name??"ss")
}
应对面试,到这里就可以了。
如果你是工作中使用,那么接下来就是经验之谈了。
网络请求数据本地化
是否存在一种不需要赋值,数据主动映射的方法,这样我们可以直接写网络请求,然后什么也不用管,数据就直接映射到model里,还进行了本地化?用的时候直接从Core Data取?
是的,有
基本上的流程就是 Data ->JSONModel ->NSManagedObject-> Core Data.
你需要将Core Data 中的ENTITIES,relationship使用的非常熟练,是非常熟练。
如果上一步做的没有问题,那么你工程中会出现好多没有属性和方法的NSManagedObject,你的Core Data 界面也非常有条理,ENTITIES之间关联的线也连好了。
下一步
是否存在JSONModel-NSManagedObject有一种关联,那么问题基本就解决了。
是的,有
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互相转换,。
下载完库,你会发现,百度里好像没有几个人对这个库进行解释。
有两种人,
1.如果你是按照(AppDelegate.persistentContainer.viewContext ),那么你就不需要去了解它的方法介绍。
2.如果你是按照(let moc =NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) ),那么你可能会做很多无用功,你需要读代码特别熟练,你去下载源码Demo多看看也就会了。
不用问,我肯定用的是第一种。
如果你拿到了Data数据,可以直接调用 objects(fromJSONData: data, inContext:NSManagedObjectContext).它就可以给你返回。
如果你处理的JSONObject,那么你需要使用它的另外两个方法。
好了,。
你会发现能正常存储,读取,但是ENTITIES中的数据都是nil。
接下来你就需要注意了,你需要到Core Data 界面配置JSONKeyPath。
每一个Attributes都配置,注意 ,是添加Attributes中User Info ,“+” 加上JSONKeyPath,value为你网络请求数据中的key。
每一个relationship都需要配置,配置原则和Attributes一样。
非常完美,不用垒代码真好。
最后,
try context.save()
我希望你在操作过Core Data数据后,能够优美的调用save方法。