ios - 获取核心数据时间歇性崩溃

标签 ios iphone swift core-data crash-reports

我有一个已发布的应用程序,在执行核心数据获取时偶尔会崩溃。我知道崩溃发生在哪里,但我无法弄清楚为什么会发生。

应用程序使用选项卡 View Controller ,并且崩溃发生在以 tableView 作为初始 View 的选项卡中。因为只要应用程序中其他地方的数据发生更改,我就需要更新此 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

我无法在我的设备或模拟器上重现崩溃,所以我真的不知道如何修复这个错误。对于解决此问题的任何建议,我将不胜感激。

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

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
    }

}

最佳答案

请允许我针对我认为正在发生的事情提出一个理论。我可能对您的项目做出了错误的假设,所以请纠正我的错误。您可能了解以下所有内容,但我试图做到彻底,所以请原谅任何明显的内容。

您有一个calories模型类的属性,该属性作为可选属性实现,在模型级别没有默认值。类似于下面的内容。

Optional attribute

您也没有实现 func validateCalories(calories: AutoreleasingUnsafeMutablePointer<AnyObject?>, error: NSErrorPointer) -> Bool在你的托管对象子类中,所以可能用nil保存一个实例(记住Core Data仍然是Objective-C,所以你的Double Swift属性是Core Data中的一个NSNumber,所以nil是完美的对于可选属性有效)。

You can specify that an attribute is optional — that is, it is not required to have a value. In general, however, you are discouraged from doing so — especially for numeric values (typically you can get better results using a mandatory attribute with a default value — in the model — of 0). The reason for this is that SQL has special comparison behavior for NULL that is unlike Objective-C's nil. NULL in a database is not the same as 0, and searches for 0 will not match columns with NULL.

至此,验证假设就很容易了。只需在模拟器中编辑应用程序用于 Core Data 的 SQLite 数据库并设置 calories单个记录的属性为null看看你是否以同样的方式崩溃。

如果是这样,请在下一个版本上进行核心数据迁移并删除 Optional型号属性上的指定并提供 Default值为零。

关于ios - 获取核心数据时间歇性崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51793477/

相关文章:

iphone - NSXMLParserDelegate 方法未被调用

ios - "Tap To Resume"暂停文本 SpriteKit

ios - 导航栏与 iphone 8 模拟器的 swift xcode 中的状态栏发生碰撞

ios - 设置一些气泡的最小宽度

ios - Xcode中的子项目有什么意义?

iphone - 单个约束的自动布局约束错误

iphone - UIWebView webViewDidFinishLoad 没有被调用 iOS

ios - 如何为 CATextLayer 的字符串设置动画?

html - 当 NSURLResponse 对于 textEncodingName 返回 nil 时检测 HTML 编码

ios - 导入证书和配置文件时出现问题