基于核心数据中基础实体属性的计算属性

Ste*_*ord 2 core-data swift

我有一个核心数据模型,其实体 \xe2\x80\x9cDay\xe2\x80\x9d 包含零个或多个 \xe2\x80\x9cWorkout\xe2\x80\x9ds。日实体具有许多属性,这些属性是通过对所有锻炼进行求和来计算的。例如,totalKM 将所有锻炼的 km 属性值相加。

\n\n

我只能 \xe2\x80\x99t 让它工作。

\n\n

我可以通过重写 value(forKey) 方法来显示总计:

\n\n
public override func value(forKey key: String) -> Any? {\n    if(key == "swimSeconds"){\n        var swimSeconds: Int64 = 0\n        if let array = workouts?.allObjects{\n            for w in array{\n                let workout: Workout = w as! Workout\n                if (workout.sport == Workout.SPORT.Swim.rawValue){\n                    swimSeconds += workout.seconds\n                }\n            }\n        }\n        return swimSeconds\n    }else{\n        return super.value(forKey: key)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,这意味着如果基础锻炼发生变化,这不会反映在“日”实体中显示的总计值中。而且感觉这不是正确的做法 - 我不应该\xe2\x80\x99t需要覆盖这个方法。

\n\n

我\xe2\x80\x99ve尝试使用Xcode创建NSManagedObject子类,但这创建了一个包含所有@NSManaged变量的扩展,但这只会给出大量错误\xe2\x80\x9cExtensions可能不包含存储的属性\xe2\x80\x9d仅通过删除@NSManaged 来修复。

\n\n

我尝试创建自己的 Extension to Day 并拥有一个只有 getter 的属性:

\n\n
var totalSwimSeconds: Int64{\n    get{\n        var swimSeconds: Int64 = 0\n        if let array = workouts?.allObjects{\n            for w in array{\n                let workout: Workout = w as! Workout\n                if (workout.sport == Workout.SPORT.Swim.rawValue){\n                    swimSeconds += workout.seconds\n                }\n            }\n        }\n        return swimSeconds\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

此方法会产生以下运行时错误:\nentity Day 与键“totalSwimSeconds”的键值编码不兼容

\n\n

I\xe2\x80\x99ve 在线搜索,看看是否可以找到这样的示例,但所有示例都只是给定实体内组合属性的简单情况(例如出生日期的年龄、名字和姓氏的全名)查看其他实体中包含的属性的更复杂的计算。

\n\n

如何在核心数据中创建计算属性,该属性将从基础实体计算并在更新时更新?

\n\n

[我正在使用 Xcode 9 和 Swift 4]

\n

vad*_*ian 5

在 Swift 4 中添加属性@objcdynamic以使计算属性键值编码兼容。

@objc dynamic var totalSwimSeconds: Int64 { ...
Run Code Online (Sandbox Code Playgroud)

编辑:

观察workouts属性keyPathsForValuesAffectingValue覆盖Day

override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
    switch key {
    case "totalSwimSeconds": return Set(["workouts"])
    default: return super.keyPathsForValuesAffectingValue(forKey:key)
    }
}
Run Code Online (Sandbox Code Playgroud)