对核心数据执行提取时出现间歇性崩溃

时间:2018-08-10 20:11:54

标签: ios iphone swift core-data crash-reports

我有一个已发布的应用程序,执行核心数据提取时有时会崩溃。我知道崩溃发生的位置,但是我不知道为什么发生崩溃。

该应用程序使用选项卡视图控制器,并且崩溃发生在以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()
}

这是调用堆栈的图像。

call stack

我无法在设备或模拟器上重新创建崩溃,因此我真的不知道如何解决此错误。对于解决此问题,我将不胜感激。

这是核心数据模型中calories属性的屏幕截图。

enter image description here

这是用于创建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
    }

}

1 个答案:

答案 0 :(得分:0)

允许我就我认为正在发生的事情提出一个理论。我可能对您的项目做出了错误的假设,因此请更正我弄错的任何事情。您可能会理解以下所有内容,但我正在尝试更详尽的内容,因此,请原谅任何明显的事情。

您有模型类的calories属性,该属性作为可选属性实现,在模型级别没有默认 。像下面这样。

Optional attribute

您也不在托管对象子类中实现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值为零。