我有一个已发布的应用程序,执行核心数据提取时有时会崩溃。我知道崩溃发生的位置,但是我不知道为什么发生崩溃。
该应用程序使用选项卡视图控制器,并且崩溃发生在以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
}
}
答案 0 :(得分:0)
允许我就我认为正在发生的事情提出一个理论。我可能对您的项目做出了错误的假设,因此请更正我弄错的任何事情。您可能会理解以下所有内容,但我正在尝试更详尽的内容,因此,请原谅任何明显的事情。
您有模型类的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的列。
在这一点上,很容易确认该假设。只需在模拟器中编辑应用程序用于核心数据的SQLite数据库,并将单个记录的calories
属性设置为null
,即可查看是否以相同的方式崩溃。
如果是这样,请在您的下一个版本中进行核心数据迁移,并删除模型属性上的Optional
指定,并提供一个Default
值为零。