ios - 对核心数据执行提取时间歇性崩溃
问题描述
我有一个已发布的应用程序,在执行核心数据提取时偶尔会崩溃。我知道崩溃发生在哪里,但是我不知道为什么会发生。
该应用程序使用选项卡视图控制器,并且崩溃发生在以 tableView 作为初始视图的选项卡中。因为每当数据在应用程序的其他地方发生更改时,我都需要更新这个 tableView,所以我在 viewWillAppear 方法中执行获取。
以下是相关代码:
lazy var fetchedResultsController: NSFetchedResultsController<Day> = {
let request: NSFetchRequest<Day> = Day.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
request.predicate = NSPredicate(format: "calories > %@", "0")
let frc = NSFetchedResultsController(fetchRequest: request, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
return frc
}()
override func viewWillAppear(_ animated: Bool) {
do {
try fetchedResultsController.performFetch()
} catch {
print("Could not fetch results")
}
tableView.reloadData()
}
这是调用堆栈的图像。
我无法在我的设备或模拟器上重新创建崩溃,所以我真的不知道如何修复这个错误。我将不胜感激有关解决此问题的任何建议。
这是calories
核心数据模型中属性的屏幕截图。
这是创建Day
实体的类方法。
class func dayWithInfo(date: Date, inManagedContext context: NSManagedObjectContext) -> Day {
let defaults = UserDefaults.standard
let formatter = DateFormatter()
formatter.dateStyle = .full
let dateString = formatter.string(from: date)
let request: NSFetchRequest<Day> = Day.fetchRequest()
request.predicate = NSPredicate(format: "dateString = %@", dateString)
if let day = (try? context.fetch(request))?.first {
if let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date()) {
// Update the macro goals if the date is the current or future date
if date > yesterday {
day.proteinGoal = defaults.double(forKey: Constants.UserDefaultKeys.proteinValueKey)
day.carbohydrateGoal = defaults.double(forKey: Constants.UserDefaultKeys.carbohydratesValueKey)
day.lipidGoal = defaults.double(forKey: Constants.UserDefaultKeys.lipidsValueKey)
day.fiberGoal = defaults.double(forKey: Constants.UserDefaultKeys.fiberValueKey)
day.calorieGoal = defaults.double(forKey: Constants.UserDefaultKeys.caloriesValueKey)
}
}
return day
} else {
let day = Day(context: context)
// Set the date as a string representation of a day
day.dateString = dateString
day.date = date
// Set the calorie and macronutrient goals from user defaults
day.proteinGoal = defaults.double(forKey: Constants.UserDefaultKeys.proteinValueKey)
day.carbohydrateGoal = defaults.double(forKey: Constants.UserDefaultKeys.carbohydratesValueKey)
day.lipidGoal = defaults.double(forKey: Constants.UserDefaultKeys.lipidsValueKey)
day.fiberGoal = defaults.double(forKey: Constants.UserDefaultKeys.fiberValueKey)
day.calorieGoal = defaults.double(forKey: Constants.UserDefaultKeys.caloriesValueKey)
// Creat meals and add their ralationship to the day
if let meals = defaults.array(forKey: Constants.UserDefaultKeys.meals) as? [String] {
for (index, name) in meals.enumerated() {
_ = Meal.mealWithInfo(name: name, day: day, slot: index, inManagedContext: context)
}
}
return day
}
}
解决方案
请允许我提出一个关于我认为正在发生的事情的理论。我可能对您的项目做出了错误的假设,所以请纠正我弄错的任何地方。您可能了解以下所有内容,但我正在努力做到彻底,所以请原谅任何明显的事情。
您有一个calories
模型类的属性,该属性作为可选属性实现,在模型级别没有默认值。像下面这样的东西。
您也没有func validateCalories(calories: AutoreleasingUnsafeMutablePointer<AnyObject?>, error: NSErrorPointer) -> Bool
在托管对象子类中实现,因此可以使用 nil 保存实例(请记住,Core Data 仍然是 Objective-C,因此您的 Double Swift 属性是 Core Data 中的 NSNumber,因此 nil 完全适用于一个可选属性)。
您可以指定一个属性是可选的——也就是说,它不需要有一个值。但是,通常不鼓励您这样做——尤其是对于数值(通常,您可以使用具有默认值(在模型中)为 0 的强制属性获得更好的结果)。原因是 SQL 对 NULL 有特殊的比较行为,这与 Objective-C 的 nil 不同。数据库中的 NULL 与 0 不同,搜索 0 将不会匹配具有 NULL 的列。
在这一点上,很容易证实这个假设。只需在模拟器中编辑应用程序用于 Core Data 的 SQLite 数据库,并将calories
单个记录的属性设置为,null
然后查看是否以相同的方式崩溃。
如果是这样,请在下一个版本中执行 Core Data 迁移并删除Optional
模型属性上的指定并提供Default
零值。
推荐阅读
- node.js - IntelliJ Ultimate Yarn 需要 nodejs 4 或更高版本
- sql - 将用户添加到 db_accessadmin 而不添加到 db_owner 的权限
- python - Flask 的 SQLAlchemy().drop_all 不起作用?
- mysql - 具有非唯一索引的 UPSERT
- javascript - sqlite在win7离线时将数据库文件存储在哪里?
- python - 为什么我在多索引的索引中有空项目
- vue.js - 使用 Vuex 将数据从 FormulateForm 传递到映射操作
- python - 想要让登录用户(客户)使用 __init__ 方法从下拉列表中过滤 Django
- java - 如何在 Android 上阅读收到的彩信和短信
- python - 询问用户他/她是否想再次玩,但输入 yes 只是再次重复这个问题,而不是重新开始