保存在 Core Data 中的 iOS 数据在启动后无法保存

Posted

技术标签:

【中文标题】保存在 Core Data 中的 iOS 数据在启动后无法保存【英文标题】:iOS data saved in Core Data doesn't survive launch 【发布时间】:2020-06-30 22:41:40 【问题描述】:

编辑 *********

2020 年 7 月 9 日下午 1:39(太平洋标准时间)

我有我认为足够的应用程序的最小工作可复制版本,可在以下位置获得:

https://github.com/Rattletrap99/penny-game-test

编辑 *********

我正在构建一个游戏,用户可以在其中创建硬币作为各种成就的奖励。硬币在 Core Data 中保存为托管对象,具有各种属性。在游戏过程中,出于各种原因检索它们、修改它们的属性等。

一切都完美地保存和检索,直到我退出并重新启动,此时持久存储中不存在任何数据。无论是使用模拟器还是设备都是如此。

我通常保存到 Core Data 的方法是:

func saveIt()
    guard let appDelegate =
        UIApplication.shared.delegate as? AppDelegate else 
            return
    
    appDelegate.saveContext()

调用者:

func saveContext () 
    let context = persistentContainer.viewContext
    if context.hasChanges 
        do 
            try context.save()
            savedFlag += 1
            
            let coinFetchRequest =
                NSFetchRequest<NSManagedObject>(entityName: "Coin")

            let savedCoins = try context.fetch(coinFetchRequest) as! [Coin]
            
            print("In appDelegate, saveContext, after context.save, there are \(savedCoins.count) coins.")
            print("Successfully saved in appDelegate \(String(describing: savedFlag)) times")

            
            
         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.
            let nserror = error as NSError
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        
    

我在代码中输入的每个print() 语句都会确认保存,但是当我检索(重新启动时)时,通过类似于以下代码:

    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    let issuerFetchRequest =
        NSFetchRequest<NSManagedObject>(entityName: "Issuer")
    let coinFetchRequest =
        NSFetchRequest<NSManagedObject>(entityName: "Coin")

    
    do 
        let issuers = try context.fetch(issuerFetchRequest) as! [Issuer]
        print(" ###   Upon startup, there are \(issuers.count) Issuers in CD")
        
        let coins = try context.fetch(coinFetchRequest) as! [Coin]
        print(" ####   Upon startup, there are \(coins.count) Coins in CD")

     catch let error as NSError 
        print("Could not fetch. \(error), \(error.userInfo)")
    

我明白了:

 ###   Upon startup, there are 0 Issuers in CD
 ####   Upon startup, there are 0 Coins in CD

另外,我尝试保存在applicationWillTerminate 以确保在退出前保存数据:

func applicationWillTerminate(_ 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()
    
    let issuerFetchRequest =
        NSFetchRequest<NSManagedObject>(entityName: "Issuer")
    let coinFetchRequest =
        NSFetchRequest<NSManagedObject>(entityName: "Coin")

    do 
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

        let issuers = try context.fetch(issuerFetchRequest) as! [Issuer]
        let coins = try context.fetch(coinFetchRequest) as! [Coin]

        print("\\\\ Upon quit, there are \(issuers.count) Issuers in CD")
        print("\\\\ Upon quit, there are \(coins.count) Coins in CD")

     catch let error as NSError 
        print("Could not fetch. \(error), \(error.userInfo)")
    

但是,print() 声明没有打印出来,这让我相信 applicationWillTerminate 永远不会被解雇。

我应该提到IssuerCointo many 关系,并且我确保在创建和保存Coin 之前存在Issuer

非常感谢任何帮助!

【问题讨论】:

你能提交一个最小的可复现版本的代码到 git 吗? 另外,您的核心数据存储是否由 sqlite 支持?您可以尝试使用 sqlite 浏览器检查 sqlite 文件,以检查数据是否正确存储在那里。如果是,那么您可以缩小到代码的检索部分。 您确定您使用的是正确的提取请求吗? docs 表示 与上下文的持久存储协调器关联的模型必须包含一个名为 entityName 的实体。 或者,您的“xcdatamodel”中的每个实体都有一个对应的同名类。您可以尝试使用 'let yourEntityFetchRequest: NSFetchRequest = YourClass.fetchRequest()',参见 here. @ReinhardMänner -- 最低工作时间:github.com/Rattletrap99/penny-game-test。但是,尽管多次尝试,我似乎无法添加 xcdatamodel 文件。任何指导表示赞赏... @ReinhardMänner -- 我尝试根据您的建议更改 fetchRequest,但结果是一样的。持久存储中的零数据... 【参考方案1】:

记得在你的 AppDelegate 中加载 persistentContainer。你可以在 Xcode 中创建一个内置 CoreData 的项目。

// MARK: - Core Data stack

lazy var persistentContainer: 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.
    */
    let container = NSPersistentContainer(name: "CoreDataModelName")
    container.loadPersistentStores(completionHandler:  (storeDescription, error) in
        if let error = error as NSError? 
            // 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)")
        
    )
    return container
()

// MARK: - Core Data Saving support

func saveContext () 
    let context = persistentContainer.viewContext
    if context.hasChanges 
        do 
            try context.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.
            let nserror = error as NSError
            fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
        
    

【讨论】:

是的,先生,这正是我的 appDelegate 的样子。我从一开始就将项目创建为 Core Data 应用程序。非常感谢您的回复,但恐怕这不会让我更进一步。 您可以尝试调整数据源类型和位置并验证它是否已保存在模拟器中。 developer.apple.com/library/archive/documentation/Cocoa/… 也可以将可重现的版本上传到我们可以看到的 git 吗? @Stephen Zyszkiewicz--为我的缺席道歉。我以前从未创建过可重现的版本,也没有使用过 gitHub。我确实在本地使用源代码控制。可重现的版本是否意味着注释掉所有非重要代码? 是的,请把源代码上传到我们可以下载的地方【参考方案2】:

在将自己打得半死的时间超过我承认的时间后,我在AppDelegate.swift的这三行中找到了错误:

    description.shouldInferMappingModelAutomatically = true
    description.shouldMigrateStoreAutomatically = true
    container.persistentStoreDescriptions = [description]

一旦这些被删除,一切都恢复正常。我想说我理解为什么,但老实说,删除这些线条是一种绝望的行为。具有讽刺意味的是,我添加了这些行是为了纠正早期从 Core Data 获取的问题。

非常感谢所有贡献的人!

【讨论】:

以上是关于保存在 Core Data 中的 iOS 数据在启动后无法保存的主要内容,如果未能解决你的问题,请参考以下文章

iOS 后台保存在 Core Data 中

iOS Core Data 保存上下文错误

Core Data基础

Core Data-备用

Core Data浅谈初级入门

ios Core Data - 我的保存按钮不起作用