Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 当同步期间存在大量字段和密集更新时,CoreData的更好模型_Ios_Swift_Core Data - Fatal编程技术网

Ios 当同步期间存在大量字段和密集更新时,CoreData的更好模型

Ios 当同步期间存在大量字段和密集更新时,CoreData的更好模型,ios,swift,core-data,Ios,Swift,Core Data,我最近在我的应用程序中遇到了一个大麻烦: 在我的应用程序中,每个单元都有将近100个属性需要保留,其中一些属性可能会非常频繁地更新,甚至在初始同步过程中,每个单元在几秒钟内更新数十次或更多。由于将它们放在一个表中可能会非常低效,并且其中一些更可能被重新同步,我尝试根据功能和可能的更新频率将单元拆分为5-6个较小的表,每个表都有8-32个字段,还有一个用于主键。为了防止主线程阻塞,我还创建了一个后台上下文来更新数据。无论何时调用context.save(),主上下文都会与后台上下文合并 然而,当我

我最近在我的应用程序中遇到了一个大麻烦:

在我的应用程序中,每个单元都有将近100个属性需要保留,其中一些属性可能会非常频繁地更新,甚至在初始同步过程中,每个单元在几秒钟内更新数十次或更多。由于将它们放在一个表中可能会非常低效,并且其中一些更可能被重新同步,我尝试根据功能和可能的更新频率将单元拆分为5-6个较小的表,每个表都有8-32个字段,还有一个用于主键。为了防止主线程阻塞,我还创建了一个后台上下文来更新数据。无论何时调用
context.save()
,主上下文都会与后台上下文合并

然而,当我尝试进行批量测试时,比如说表中的50个单元,我发现UI仍然被严重阻塞,因为主上下文(负责根据数据库中的单元属性获取UI布局)正在忙于合并密集的后台上下文更新

我还发现,当重新加载数据时,即使只有50个单元,也会有10000多个合并,我查看了一下,发现每当调用一个
context.save()
,我的应用程序就会收到9个
NSManagedObjectContextDidSave
通知,这意味着大多数合并实际上是多余的

我还想知道是否有更好的方法(无论是从上下文管理还是从数据表规范化设计的角度)来处理数据库结构问题:虽然记录的数量不是很大(可能总共少于1000条),但有相当多的字段需要维护。处理巨大记录的技术已经足够成熟,可以提高效率,但对于处理巨大字段,我可以找到非常有限的资源,更不用说CoreData中的资源了

不确定更改为定期上下文保存是否是一个好主意,因为它是一个蓝牙应用程序,数据可以在任何时间、任何线程中进出,因此本地数据库中即时数据的准确性对于采取正确的操作至关重要。但是,如果我不立即通过
context.save()
提交,则不会进行更改

总而言之,这里有两个问题:

  • 为什么在只调用一次
    context.save()
    时会触发多个
    NSManagedObjectContextDidSave

  • 在处理这种数据库结构(每个记录有很多字段)时,有没有更好的模型可以在UI体验和数据准确性之间实现更好的权衡

  • 顺便说一句,为了提高UI性能,我确实使用了
    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()