Core Data 有时会丢失数据

Posted

技术标签:

【中文标题】Core Data 有时会丢失数据【英文标题】:Core Data sometimes loses data 【发布时间】:2019-02-09 18:58:24 【问题描述】:

我们正在通过 Citrix Secure Hub 运行应用程序,似乎有时会出现回滚并丢失 CoreData 中的一些数据。

据我了解,CoreData 拥有所有对象的工作副本,有时它会尝试将其保留在文件系统上。

尝试模拟该行为但没有任何成功,我们在测试环境中找不到任何数据丢失或回滚数据。

那么有没有办法强制 ios 在磁盘上写入当前的“工作副本”,以防止在使用过多内存时丢失任何数据(并且可能会崩溃)?我们在

之后调用我们的保存函数

正如我们已经发现的那样:

我们没有使用:

func applicationWillResignActive(_ application: UIApplication) 
      print("applicationWillResignActive")

要保存上下文,这可能是个问题吗(我们已经在每个创建对象后保存上下文)?

目前,当无法保存上下文时,我们并没有真正处理问题,是否有任何建议如何在生产环境中处理?应用程序崩溃以防止用户因数据丢失而苦苦挣扎,这是一件好事吗?

编辑:这是使用的核心数据处理程序:

import Foundation
import CoreData

let context = CoreDataManager.shared.managedObjectContext

func saveContext(_ completion: (() -> Void)? = nil) 

     CoreDataManager.shared.save(completion)


func saveContextSync() 

     CoreDataManager.shared.saveSync()


class CoreDataManager: NSObject 

    static let shared = CoreDataManager()

    lazy var managedObjectContext: NSManagedObjectContext = 

    var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)

    managedObjectContext.persistentStoreCoordinator = persistentStoreCoordinator

    return managedObjectContext
()

还有我们的保存功能:

@objc func save(_ completion: (() -> Void)?) 

    saveAsync(completion)


func saveAsync(_ completion: (() -> Void)?) 

    func save() 

        context.perform 
            do  try context.save() 
            catch 
              // HERE WE NEED TO HANDLE IT FOR A PRODUCTIVE ENVIRONMENT
            

            completion?()
        
    

    if Thread.isMainThread 
        save()
     else 
        DispatchQueue.main.async 
            save()
        
    



func saveSync() 

    func save() 
        context.performAndWait 
            do  try context.save() 
            catch  print(error)
                // TRY TO REPRODUCE MEMORY LOSS APP TO SEE WHAT HAPPENS
                abort()
            
        
    

    if Thread.isMainThread 
        save()
     else 
        DispatchQueue.main.sync 
            save()
        
    

编辑 2:Objective C 中的这个问题应该非常相似:

Core Data reverts to previous state without apparent reason

编辑 3:似乎没有崩溃,一些用户告诉我他们正在添加数据,然后只需按主页按钮,几个小时后,上一个“任务”的数据就会丢失。

【问题讨论】:

可能我不明白你的问题,但不是do try managedContext.save() catch let error as NSError // Handle error 你在找什么? 不,我不这么认为 - 如果您说:尝试 managedContext.save() 您只是要求保存上下文,但您无法控制它是否真的保存到磁盘。我们遇到同样的问题,例如:***.com/questions/33600914/data-loss-core-data 感谢您指出这一点 - 我是核心数据的新手,不知道这个问题! 核心数据确实立即保存到磁盘——除非您使用嵌套上下文。你是?如果是这样,请参阅例如***.com/questions/10428351/… 感谢 matt 的回答,但我看到我们没有使用嵌套上下文。有没有机会证明这一点? 【参考方案1】:

有三种可能的原因。

写入冲突

核心数据通常希望以单一同步方式完成写入。如果您同时以多种方式写入同一个对象(即使它们涉及不同的属性并且没有严格冲突),这将是合并冲突。您可以设置合并策略(默认值为“错误” - 意味着不应用更改)但这确实是一个糟糕的解决方案,因为您告诉核心数据默默地丢失信息。有关防止合并冲突的设置,请参阅 NSPersistentContainer concurrency for saving to core data。

关闭未保存数据的应用

如果您正确设置核心数据,则不会发生这种情况。将核心数据设置为仅从“viewContext”读取并以单一同步方式写入的正确方法。每次写入都在单个原子块中完成,并且 UI 仅在保存后更新。如果您正在显示来自未保存到磁盘的上下文中的信息,这可能是一个问题。例如,您的应用似乎只使用一个主线程上下文进行读取和写入。对该上下文进行更改而不调用 save 将使应用程序处于仅在内存中而不在磁盘上发生重大更改的状态。

保存到磁盘时出错

这是迄今为止最罕见的事件,但有些用户的磁盘真的很满。如果发生这种情况,您将无能为力。磁盘上实际上没有剩余空间。一般来说,正确的做法是告诉用户并让它保持不变。


如果不了解您的特定设置,很难确定您的问题是什么。我会推荐以下设置:

    使用NSPersistentContainer 只读取viewContext,从不写入。 为写入核心数据创建一个操作队列 为每个操作创建一个上下文,对其进行更改,然后保存。不要将任何托管对象传入或传出这些块。

这应该可以解决除第三个问题之外的所有问题。

【讨论】:

以上是关于Core Data 有时会丢失数据的主要内容,如果未能解决你的问题,请参考以下文章

Core Data 对象有时不会保存到永久存储中

Core Data iCloud 获取有时不会产生数据(iOS)

将 Core Data XML 存储同步到磁盘

当涉及多个设备时,Core Data iCloud 迁移“丢失”数据

如何在不丢失数据的情况下更改 Core Data SQLite 数据库的结构

SwiftUI:Core Data @FetchRequest 和 List 显示托管对象 - 在 lockscreemn 上丢失数据