Ios 当同步期间存在大量字段和密集更新时,CoreData的更好模型
我最近在我的应用程序中遇到了一个大麻烦: 在我的应用程序中,每个单元都有将近100个属性需要保留,其中一些属性可能会非常频繁地更新,甚至在初始同步过程中,每个单元在几秒钟内更新数十次或更多。由于将它们放在一个表中可能会非常低效,并且其中一些更可能被重新同步,我尝试根据功能和可能的更新频率将单元拆分为5-6个较小的表,每个表都有8-32个字段,还有一个用于主键。为了防止主线程阻塞,我还创建了一个后台上下文来更新数据。无论何时调用Ios 当同步期间存在大量字段和密集更新时,CoreData的更好模型,ios,swift,core-data,Ios,Swift,Core Data,我最近在我的应用程序中遇到了一个大麻烦: 在我的应用程序中,每个单元都有将近100个属性需要保留,其中一些属性可能会非常频繁地更新,甚至在初始同步过程中,每个单元在几秒钟内更新数十次或更多。由于将它们放在一个表中可能会非常低效,并且其中一些更可能被重新同步,我尝试根据功能和可能的更新频率将单元拆分为5-6个较小的表,每个表都有8-32个字段,还有一个用于主键。为了防止主线程阻塞,我还创建了一个后台上下文来更新数据。无论何时调用context.save(),主上下文都会与后台上下文合并 然而,当我
context.save()
,主上下文都会与后台上下文合并
然而,当我尝试进行批量测试时,比如说表中的50个单元,我发现UI仍然被严重阻塞,因为主上下文(负责根据数据库中的单元属性获取UI布局)正在忙于合并密集的后台上下文更新
我还发现,当重新加载数据时,即使只有50个单元,也会有10000多个合并,我查看了一下,发现每当调用一个context.save()
,我的应用程序就会收到9个NSManagedObjectContextDidSave
通知,这意味着大多数合并实际上是多余的
我还想知道是否有更好的方法(无论是从上下文管理还是从数据表规范化设计的角度)来处理数据库结构问题:虽然记录的数量不是很大(可能总共少于1000条),但有相当多的字段需要维护。处理巨大记录的技术已经足够成熟,可以提高效率,但对于处理巨大字段,我可以找到非常有限的资源,更不用说CoreData中的资源了
不确定更改为定期上下文保存是否是一个好主意,因为它是一个蓝牙应用程序,数据可以在任何时间、任何线程中进出,因此本地数据库中即时数据的准确性对于采取正确的操作至关重要。但是,如果我不立即通过context.save()
提交,则不会进行更改
总而言之,这里有两个问题:
context.save()
时会触发多个NSManagedObjectContextDidSave
NSFetchedResultsController
,但发现如果我的表有关系,它只能起到很小的作用,所以我要做的是将最重要的属性放在NSFetchedResultsController获取的主实体中,然而,这并没有多大帮助,因为主上下文仍在忙于合并
我的核心数据操作代码如下所示:
// MARK: - Example on saving a NSManagedObject in background thread
class ExampleRepository : CoreDataStack {
func updateRecordToDbInBackground(uuid: String, values: [String: Data]) {
let ctx = getBackgroundContext()
ctx.perform {
guard let resultId = self.fetchByUuid(uuid: uuid, context: ctx) else {
self.addRecordToDb(uuid: uuid, values: values, context: ctx)
self.saveContext(context: ctx, method: #function)
return
}
let result = ctx.object(with: resultId)
_ = self.updateRecordToDb(uuid: uuid, values: values, managedObject: result, context: ctx)
self.saveContext(context: ctx, method: #function)
}
}
func fetchByUuid(uuid: String, context: NSManagedObjectContext) -> NSManagedObjectID? {
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: ENTITY_NAME)
fetchRequest.predicate = NSPredicate(format: "SELF.uuid = %@", uuid)
do {
return try context.fetch(fetchRequest).first?.objectID
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
return nil
}
func updateRecordToDb(uuid: String, values: [String: Data], managedObject: NSManagedObject?, context: NSManagedObjectContext) -> NSManagedObject {
let registerObj = managedObject ?? getManagedObject(context: context)
registerObj.setValue(uuid, forKey: FIELD_NAME_UUID)
let convertedValues: NSMutableDictionary = [:]
values.forEach { e in
convertedValues.addEntries(from: e.key: e.value])
}
registerObj.setValuesForKeys(convertedValues as! [String: Data])
return registerObj
}
func addRecordToDb(uuid: String, values: [String: Data], managedObject: NSManagedObject?, context: NSManagedObjectContext) -> NSManagedObject {
// add is generally similar to updateRecoredToDb, but adding some relationships to other entities
}
}
// MARK: - Core data stack
class CoreDataStack {
init(){
NotificationCenter.default.addObserver(self, selector: #selector(self.contextDidSaveContext), name: NSNotification.Name.NSManagedObjectContextDidSave, object: nil)
}
deinit{
NotificationCenter.default.removeObserver(self)
}
var _context: NSManagedObjectContext {
return SingletonContext.sharedInstance._context
}
var _backgroundContext: NSManagedObjectContext {
return SingletonContext.sharedInstance._backgroundContext
}
func saveContext(context: NSManagedObjectContext, method: String) {
guard context.hasChanges else {
return
}
context.perform {
do {
try context.save()
print("Context called from \(method) saved!")
} catch let error as NSError {
print("Could not save context called from \(method), Error: \(error), \(error.userInfo)")
}
}
}
@objc func contextDidSaveContext(notification: NSNotification) {
// The question is here: This func is called nine times when the context.save() were called once.
let sender = notification.object as! NSManagedObjectContext
if sender === self._context {
print("******** Merge background context with main ********")
self._backgroundContext.perform {
self._backgroundContext.mergeChanges(fromContextDidSave: notification as Notification)
}
}
else if sender === self._backgroundContext {
print("******** main main context with background ********")
self._context.perform {
self._context.mergeChanges(fromContextDidSave: notification as Notification)
}
}
}
}
class SingletonContext {
static let sharedInstance = SingletonContext()
var _persistentStoreCoordinator: NSPersistentStoreCoordinator {
return (UIApplication.shared.delegate as! AppDelegate).persistentContainer.persistentStoreCoordinator
}
lazy var _context: NSManagedObjectContext = {
let ctx = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.mainQueueConcurrencyType)
ctx.persistentStoreCoordinator = self._persistentStoreCoordinator
return ctx
}()
lazy var _backgroundContext: NSManagedObjectContext = {
let ctx = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.privateQueueConcurrencyType)
ctx.persistentStoreCoordinator = self._context.persistentStoreCoordinator
return ctx
}()
}
//标记:-在后台线程中保存NSManagedObject的示例
类ExampleRepository:CoreDataStack{
func updateRecordToBinBackground(uuid:String,值:[String:Data]){
设ctx=getBackgroundContext()
执行{
guard let resultId=self.fetchByUuid(uuid:uuid,context:ctx)else{
addRecordToDb(uuid:uuid,值:值,上下文:ctx)
saveContext(上下文:ctx,方法:#函数)
返回
}
让result=ctx.object(带:resultId)
_=self.updateRecordToDb(uuid:uuid,value:values,managedObject:result,context:ctx)
saveContext(上下文:ctx,方法:#函数)
}
}
func fetchByUuid(uuid:String,context:NSManagedObjectContext)->nsmanagedobjectd{
let fetchRequest=NSFetchRequest(entityName:ENTITY\u NAME)
fetchRequest.predicate=NSPredicate(格式:“SELF.uuid=%@”,uuid)
做{
返回try context.fetch(fetchRequest).first?.objectID
}将let错误捕获为NSError{
打印(“无法获取.\(错误),\(错误.userInfo)”)
}
归零
}
func updateRecordToDb(uuid:String,值:[字符串:数据],managedObject:NSManagedObject?,上下文:NSManagedObjectContext)->NSManagedObject{
让registerObj=managedObject??getManagedObject(上下文:context)
registerObj.setValue(uuid,forKey:FIELD\u NAME\u uuid)
让convertedValues:NSMutableDictionary=[:]
values.forEach{e in
convertedValues.addEntries(from:e.key:e.value])
}
registerObj.SetValuesOfferKeys(转换为![String:Data]的值)
返回寄存器bj
}
func addRecordToDb(uuid:String,值:[字符串:数据],managedObject:NSManagedObject?,上下文:NSManagedObjectContext)->NSManagedObject{
//add通常类似于UpdateCoredToDB,但向其他实体添加了一些关系
}
}
//标记:-核心数据堆栈
类CoreDataStack{
init(){
NotificationCenter.default.addObserver(self,选择器:#选择器(self.contextDidSaveContext),名称:NSNotification.name.NSManagedObjectContextDidSave,对象:nil)
}
脱硝{
NotificationCenter.default.removeObserver(自)
}
变量上下文:NSManagedObjectContext{
返回SingletonContext.sharedInstance.\u context
}
var_backgroundContext:NSManagedObjectContext{
返回SingletonContext.sharedInstance.\u backgroundContext
}
func saveContext(上下文:NSManagedObjectContext,方法:String){
guard context.has更改其他内容{
返回
}
context.perform{
做{
尝试context.save()