如何在Swift 3中将自定义类保存为CoreData实体的属性?

5

我有一个CoreData实体SavedWorkout。它具有以下属性: enter image description here

completionCounter是一个Bool数组,而workout是一个名为Workout的自定义类。

我是这样保存我的数据的:

let saveCompletionCounter = currentCompletionCounter
let saveDate = Date() as NSDate
let saveRoutineIndex = Int16(currentWorkoutRoutine)
let saveWorkout = NSKeyedArchiver.archivedData(withRootObject: workout)

item.setValue(saveDate, forKey: "date")
item.setValue(saveWorkout, forKey: "workout")
item.setValue(saveRoutineIndex, forKey: "routineIndex")
item.setValue(saveCompletionCounter, forKey: "completionCounter")

do {
  try moc.save()
  print("save successful")
} catch {
  print("saving error")
}

其中mocNSManagedObjectContext的实例,而itemNSManagedObject的实例:

moc = appDelegate.managedObjectContext
entity = NSEntityDescription.entity(forEntityName: "SavedWorkout", in: moc)!
item = NSManagedObject(entity: entity, insertInto: moc)

根据这个这个以及这个,我已经让我的Workout类符合NSObjectNSCoding,现在它看起来像这样:

class Workout: NSObject, NSCoding {

  let name: String
  let imageName: String
  let routine: [WorkoutRoutine]
  let shortDescription: String

  required init?(coder aDecoder: NSCoder) {
    name = aDecoder.decodeObject(forKey: "name") as! String
    imageName = aDecoder.decodeObject(forKey: "imageName") as! String
    routine = aDecoder.decodeObject(forKey: "routine") as! [WorkoutRoutine]
    shortDescription = aDecoder.decodeObject(forKey: "shortDescription") as! String
  }

  func encode(with aCoder: NSCoder) {
    aCoder.encode(name, forKey: "name")
    aCoder.encode(imageName, forKey: "imageName")
    aCoder.encode(routine, forKey: "routine")
    aCoder.encode(shortDescription, forKey: "shortDescription")
  }

  init(name: String, imageName: String, routine: [WorkoutRoutine], shortDescription: String) {
    self.name = name
    self.imageName = imageName
    self.routine = routine
    self.shortDescription = shortDescription
  }
}

然而,我总是在这一行代码上遇到错误:routine: aDecoder.decodeObject...
错误提示如下: NSForwarding: warning: object 0x60800002cbe0 of class 'App.WorkoutRoutine' does not implement methodSignatureForSelector: -- trouble ahead Unrecognized selector -[FitLift.WorkoutRoutine replacementObjectForKeyedArchiver:] 为什么这会出现错误,而其他的Transformable属性不会?如何将自定义类保存为CoreData实体的属性?

你还有一个名为WorkOutRoutine的类。那个类也符合NSCoder吗? - AgRizzo
哦,好的,所以任何和所有的子类都必须符合并具有编码/解码功能?谢谢! - A_toaster
WorkoutRoutine是否符合NSCoding标准? - Giuseppe Lanza
1个回答

5
问题在于WorkoutRoutine是一个自定义类,而根据您的错误提示,它不符合NSCoding协议,因此aCoder.encode(routine, forKey: "routine")并不知道如何编码它,同样的,routine = aDecoder.decodeObject(forKey: "routine") as! [WorkoutRoutine]也不知道如何解码它。
虽然不是很相关,但请尝试更安全的方式来初始化您的编码器和解码器,因为强制解包可能会导致崩溃,如果编码器没有包含您正在查找的键(出于任何原因)。
required init?(coder aDecoder: NSCoder) {
    guard let name = aDecoder.decodeObject(forKey: "name") as? String,
        let imageName = aDecoder.decodeObject(forKey: "imageName") as? String,
        let routine = aDecoder.decodeObject(forKey: "routine") as? [WorkoutRoutine],
        let shortDescription = aDecoder.decodeObject(forKey: "shortDescription") as? String else {
            return nil
    }
    self.name = name
    self.imageName = imageName
    self.routine = routine
    self.shortDescription = shortDescription
}

谢谢你的回答!我遇到的问题之一是尝试使用Swift 3和Xcode 8,同时保持与iOS 9的兼容性。那么将自定义类保存为coredata实体的替代方法是什么? - A_toaster
1
确保拥有一个反映您类的核心数据模型将是最佳实践。您可以通过关系连接您的模型。在您的示例中,workout属性将是与包含您的workout类属性的workout实体之间的一对一关系,而routine属性将是与workoutRoutine实体之间的一对多关系。 - Giuseppe Lanza
1
如果你想使用NSCoder,请考虑使用基于文档的持久化层。CoreData可能不是最好的选择。如果你想使用Core Data,我强烈建议你将其用于它的主要目的:关系数据库。 - Giuseppe Lanza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接