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

Posted

技术标签:

【中文标题】如何在 Swift 3 中将自定义类保存为 CoreData 实体的属性?【英文标题】:How do you save a custom class as an attribute of a CoreData entity in Swift 3? 【发布时间】:2017-07-20 09:45:48 【问题描述】:

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

completionCounterBool 的数组,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)

根据this和this和this,我已经让我的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? Ohhhh 好的,所以任何和所有子类都必须符合并具有编码/解码功能?谢谢! WorkoutRoutine NSCoding 是否兼容? 【参考方案1】:

问题在于 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 实体的另一种方法是什么? 确保拥有反映您的班级的核心数据模型将是最佳实践。您可以通过关系连接您的模型。在您的示例中,锻炼属性将是与实体“锻炼”的一对一关系,该实体“锻炼”将包含锻炼类的属性,其中常规属性与锻炼例程实体是一对多的关系。 developer.apple.com/library/content/documentation/Cocoa/… 如果您想使用 NSCoder,请考虑使用基于文档的持久层。 CoreData 可能不是您的最佳选择。如果您想使用核心数据,我强烈建议您将其用于其主要目的:关系数据库。

以上是关于如何在 Swift 3 中将自定义类保存为 CoreData 实体的属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中归档和取消归档自定义对象?或者如何在 Swift 中将自定义对象保存到 NSUserDefaults?

如何在 Swift 中将内容包装为自定义视图

如何在 Swift 5 中将文档转换为自定义对象?

自定义类 Unarchive 在 Cocoa Swift 中为零

Swift 和 Xcode - 如何创建自定义标签栏图标

Swift/IOS/CoreData:如何在自动生成的 CoreData 类中将 var 定义为枚举类型?