应用程序重新启动时,数据在Core Data中不会持久

时间:2017-06-24 09:07:18

标签: ios core-data swift3

我在XCode 8,swift 3中创建的项目中第一次使用Core Data。我使用后台上下文(调用container.performBackgroundTask块...)来保存数据和主上下文以获取数据。当我的应用程序重新启动时,我保存在私有背景上下文中的数据将被删除。

请告诉我哪里出错了!!!

这里我在AppDlegate类的applicationDidEnterBackground和applicationWillTerminate方法中调用CoreDataManager类的save上下文方法:

class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
lazy var coreDataMgrInstance = CoreDataManager.sharedInstance

func applicationDidEnterBackground(_ application: UIApplication) {

    coreDataMgrInstance.saveContext()
}
func applicationWillTerminate(_ application: UIApplication) {
    coreDataMgrInstance.saveContext()
}}

这是我的Singleton类CoreDataManager来启动NSpersistentContainer

class CoreDataManager: NSObject {    
class var sharedInstance: CoreDataManager {
    struct Singleton {
        static let instance = CoreDataManager()
    }

    return Singleton.instance
}
   private override init() {

    super.init()
}

 lazy var persistentContainer: NSPersistentContainer = {

   let container = NSPersistentContainer(name: "E_CareV2")

    let description = NSPersistentStoreDescription() // enable auto lightweight migratn
    description.shouldInferMappingModelAutomatically = true
    description.shouldMigrateStoreAutomatically = true

    container.persistentStoreDescriptions = [description]

    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
    container.viewContext.automaticallyMergesChangesFromParent = true
    return container
}()
func saveContext(){

    print("saveContext")
    let context = persistentContainer.viewContext
    if context.hasChanges {
        do {
            try context.save()
        } catch {
            let nserror = error as NSError
            fatalError("Failure to save main context \(nserror), \(nserror.userInfo)")
        }
}}

现在这是我保存并从Core Data中获取数据的类

class SenderViewController: UIViewController {

var persistentContainer: NSPersistentContainer!

override func viewDidLoad() {
    super.viewDidLoad()

 persistentContainer = CoreDataManager.sharedInstance.persistentContainer
 let results = self.fetchPersistentData(entityName: "School", withPredicate: nil)
    print("results \n \(results)")       
  }

 @IBAction func enterPressed(_ sender: Any) {

  self.persistentContainer.performBackgroundTask({ (backgroundContext) in

        backgroundContext.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump

        let schoolentity = NSEntityDescription.insertNewObject(forEntityName: "School", into: backgroundContext) as! School

                schoolentity.schoolName = "ABC"
                schoolentity.schoolWebSite = "http://anywebsite.com/"

            do {
                try backgroundContext.save()
            } catch {
                fatalError("Failure to save background context: \(error)")
                }
    })
}

func fetchPersistentData(entityName: String?, withPredicate: NSPredicate?) -> [NSManagedObject]{

        let context = self.persistentContainer.viewContext
        let request: NSFetchRequest<School> = School.fetchRequest()
        let newentity = NSEntityDescription.entity(forEntityName: entityName!, in: context)

        request.entity = newentity
        request.returnsObjectsAsFaults = false

        do {
            let searchResults = try context.fetch(request) as [NSManagedObject]
            return searchResults               
        } catch {
            print("Error with request: \(error)")
        }
 return []
}

3 个答案:

答案 0 :(得分:4)

实际上,默认情况下会启用轻量级迁移,如屏幕截图enter image description here

所示

所以你可以安全地删除这些行:

let description = NSPersistentStoreDescription() // enable auto lightweight migratn
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true

container.persistentStoreDescriptions = [description]

之后一切都应该有效。

答案 1 :(得分:1)

我弄清楚是什么原因导致我的数据不能在Core Data中持久化。我在 persistentContainer 定义中使用以下4行代码来启用 LIGHTWEIGHT MIGRATION 模型:

let description = NSPersistentStoreDescription() // enable auto lightweight migratn
description.shouldInferMappingModelAutomatically = true
description.shouldMigrateStoreAutomatically = true

container.persistentStoreDescriptions = [description]

当我删除这些行时,我可以在应用重新启动时保留我的数据。 我编写了以上几行来为我的模型启用轻量级迁移,但是我没有更改模型或创建模型的新版本,使得Core Data无法在NSBundle中搜索目标模型,因此无法推断Mapping。 / p>

我仍然不确定,这将如何删除我的数据,但我会继续尝试解决这个问题并在我获得成功时发表评论......:)

答案 2 :(得分:0)

有两种方法可以使用NSPersistentContainer - 简单的方法和正确的方法。你正在混合它们导致问题。 Apple在其文档中对此有点不一致,因此您可能会感到困惑。

简单方法 - 简单的方法是仅使用viewContext来读取和写入,并且仅使用主线程。从来没有任何冲突,因为核心数据是通过单个线程评估的。这种方法的问题是你不能在后台线程中运行任何东西。因此,如果要导入大量数据,UI将会冻结。如果你有一个超级简单的应用程序(一个小任务列表?),这将工作正常,但不是我会推荐给一个严肃的应用程序。对于初学者来说,测试应用程序可以学习核心数据。

正确的方式 - 正确的方法是永远不要写入viewContext EVER。 Apple在NSPersistentContainer文档中记录了这一点(但在其模板中也为viewContext创建了一个save方法?!)。在此设置中,对核心数据的所有写入必须通过performBackgroundTask,并且必须在块结束之前在该上下文上调用save。没有合并冲突,因为所有写入都通过performBackgroundTask,内部有一个串行队列。这种设置很难正确完成。来自performBackgroundTask上下文的对象无法离开块,viewContext中的对象无法在块中使用。编译器对你没有帮助,所以你总是需要注意这一点。

您的问题是mergePolicymergePolicy是邪恶的。如果您在核心数据中存在冲突,那么您已经做错了,任何合并策略都会丢失数据。在简单的设置中没有冲突,因为它全部在一个线程上。在正确的设置中没有冲突,因为performBackgroundTask有一个队列。问题是如果您使用两个performBackgroundTask并写viewContext,您可能会遇到冲突,您将丢失数据。就个人而言,我认为最好没有mergePolicy然后崩溃然后默默地丢失数据。

<强> TL; TD: 删除方法saveContext,删除mergePolicy,不要写入viewContext