保存在 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
永远不会被解雇。
我应该提到Issuer
与Coin
有to many
关系,并且我确保在创建和保存Coin
之前存在Issuer
。
非常感谢任何帮助!
【问题讨论】:
你能提交一个最小的可复现版本的代码到 git 吗? 另外,您的核心数据存储是否由 sqlite 支持?您可以尝试使用 sqlite 浏览器检查 sqlite 文件,以检查数据是否正确存储在那里。如果是,那么您可以缩小到代码的检索部分。 您确定您使用的是正确的提取请求吗? docs 表示 与上下文的持久存储协调器关联的模型必须包含一个名为 entityName 的实体。 或者,您的“xcdatamodel”中的每个实体都有一个对应的同名类。您可以尝试使用 'let yourEntityFetchRequest: NSFetchRequest记得在你的 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 数据在启动后无法保存的主要内容,如果未能解决你的问题,请参考以下文章