【问题标题】:iOS data saved in Core Data doesn't survive launch保存在 Core Data 中的 iOS 数据在启动后无法保存
【发布时间】: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,但结果是一样的。持久存储中的零数据...

标签: ios swift core-data


【解决方案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 获取的问题。

非常感谢所有贡献的人!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多