在核心数据中检索/保存自定义对象时出错
Error with retrieve / save custom object in core data
我开发了一个 ios 应用程序,允许用户编辑乐谱 sheet 现在我想实现数据持久性以防止丢弃更改。
阅读 ios 文档,我注意到存在改进数据持久性的不同方法,我相信对我的应用程序来说最好的方法是 Core Data。
考虑到我的应用程序使用了很多自定义对象,我遇到了很多问题。
我正在尝试使用核心数据来保存一个实体,称为分数 sheet,由两个属性组成:
- 名称:字符串
- score:另一个自定义对象(Measure)的数组,由其他自定义对象(Score Element)组成
根据文档和其他 q/a 我决定在模型上使用 Trasformable 类型:
所以我声明了一个通用的 class 用作分数属性的转换器:
public class NSSecureCodingValueTransformer<T: NSSecureCoding & NSObject>: ValueTransformer {
public override class func transformedValueClass() -> AnyClass { T.self }
public override class func allowsReverseTransformation() -> Bool { true }
public override func transformedValue(_ value: Any?) -> Any? {
guard let value = value as? T else { return nil }
return try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
}
public override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? NSData else { return nil }
let result = try? NSKeyedUnarchiver.unarchivedObject(
ofClass: T.self,
from: data as Data
)
return result
}
/// The name of this transformer. This is the name used to register the transformer using `ValueTransformer.setValueTransformer(_:forName:)`
public static var transformerName: NSValueTransformerName {
let className = NSStringFromClass(T.self)
return NSValueTransformerName("DHC\(className)ValueTransformer")
}
/// Registers the transformer by calling `ValueTransformer.setValueTransformer(_:forName:)`.
public static func registerTransformer() {
let transformer = NSSecureCodingValueTransformer<T>()
ValueTransformer.setValueTransformer(transformer, forName: transformerName)
}
}
以这种方式使用 DHCMeasureValueTransformer 作为 DataModel 文件中的转换器。
问题是,当我保存时,没有发生错误,但是当我为重新启动应用程序获取数据时,我只能获取分数的名称 sheet,而分数数组是空的,就像没有元素一样放在里面(很明显,在保存之前,我尝试打印数组内容,证明我正在使用一个非空数组)
保存代码如下:
static func saveContext() {
let context = getContext()
do {
try context.save()
} catch {
print("error during the save.")
}
}
这里是实体对象的两个classes的代码:
// DataClass
@objc(ScoreSheet)
public class ScoreSheet: NSManagedObject {
static var supportsSecureCoding: Bool {
return true
}
}
//DataProperties
extension ScoreSheet {
@nonobjc public class func fetchRequest() -> NSFetchRequest<ScoreSheet> {
return NSFetchRequest<ScoreSheet>(entityName: "ScoreSheet")
}
@NSManaged public var name: String
@NSManaged public var score: [Measure]
}
Clearly Measure class 实现 NSSecureCoding 和解码和编码对象的方法。
这是措施 class 实施:
import Foundation
class Measure: NSObject, NSCoding, NSSecureCoding {
var elements : [ScoreElement] = []
var timeSig : TimeSignature
var clef : Clef
static var supportsSecureCoding = true
init(time : TimeSignature, clef : Clef) {
self.timeSig = time
self.clef = clef
}
func encode(with encoder: NSCoder) {
encoder.encode(self.elements, forKey: "elements")
encoder.encode(self.timeSig, forKey: "timeSig")
encoder.encode(self.clef, forKey: "clef")
}
required convenience init? (coder decoder: NSCoder) {
let elements = decoder.decodeObject(forKey: "elements") as! [ScoreElement]
let timeSig = decoder.decodeObject(forKey: "timeSig") as! TimeSignature
let clef = decoder.decodeObject(forKey: "clef") as! Clef
self.init(time: timeSig, clef: clef)
self.elements = elements
}
}
我不确定出了什么问题,但有几处问题需要修复,可能会影响您的结果。
首先,计算出的转换器名称与您尝试使用的转换器名称不同。当这一行执行时,T
是 Measure
,
let className = NSStringFromClass(T.self)
然后 className
会变成 MyProjectName.Measure
。计算出的转换器名称最终类似于 NSValueTransformerName(_rawValue: DHCMyProjectName.MeasureValueTransformer)
,这与您在数据模型中使用的名称不匹配。所有这些都意味着您的变压器未被使用。
但这可能并不重要,因为如果 Measure
符合 NSSecureCoding
和 Measure
的所有属性(ScoreElement
、TimeSignature
、 Clef
) 也符合NSSecureCoding
(这似乎是因为你的代码没有抛出异常),然后你不根本不需要自定义转换器。如果一个可转换的 属性 类型符合 NSSecureCoding
那么 Core Data 将自动使用 NSSecureCoding
。除非出于某种原因不想或不能符合 NSSecureCoding
,否则您不需要自定义转换器。因此,您的变压器未被使用并不重要。
至于为什么 Measure
没有在 encode/decode 过程中幸存下来,我不知道,但你可以通过消除不必要的 encode/decode 的干扰来帮助解决问题class。我还建议在 encode(with:)
和 init(coder:)
方法中的 Measure
中放置一个断点。您应该在保存和获取数据时遇到这些断点。
我开发了一个 ios 应用程序,允许用户编辑乐谱 sheet 现在我想实现数据持久性以防止丢弃更改。
阅读 ios 文档,我注意到存在改进数据持久性的不同方法,我相信对我的应用程序来说最好的方法是 Core Data。 考虑到我的应用程序使用了很多自定义对象,我遇到了很多问题。
我正在尝试使用核心数据来保存一个实体,称为分数 sheet,由两个属性组成:
- 名称:字符串
- score:另一个自定义对象(Measure)的数组,由其他自定义对象(Score Element)组成
根据文档和其他 q/a 我决定在模型上使用 Trasformable 类型:
所以我声明了一个通用的 class 用作分数属性的转换器:
public class NSSecureCodingValueTransformer<T: NSSecureCoding & NSObject>: ValueTransformer {
public override class func transformedValueClass() -> AnyClass { T.self }
public override class func allowsReverseTransformation() -> Bool { true }
public override func transformedValue(_ value: Any?) -> Any? {
guard let value = value as? T else { return nil }
return try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
}
public override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? NSData else { return nil }
let result = try? NSKeyedUnarchiver.unarchivedObject(
ofClass: T.self,
from: data as Data
)
return result
}
/// The name of this transformer. This is the name used to register the transformer using `ValueTransformer.setValueTransformer(_:forName:)`
public static var transformerName: NSValueTransformerName {
let className = NSStringFromClass(T.self)
return NSValueTransformerName("DHC\(className)ValueTransformer")
}
/// Registers the transformer by calling `ValueTransformer.setValueTransformer(_:forName:)`.
public static func registerTransformer() {
let transformer = NSSecureCodingValueTransformer<T>()
ValueTransformer.setValueTransformer(transformer, forName: transformerName)
}
}
以这种方式使用 DHCMeasureValueTransformer 作为 DataModel 文件中的转换器。 问题是,当我保存时,没有发生错误,但是当我为重新启动应用程序获取数据时,我只能获取分数的名称 sheet,而分数数组是空的,就像没有元素一样放在里面(很明显,在保存之前,我尝试打印数组内容,证明我正在使用一个非空数组)
保存代码如下:
static func saveContext() {
let context = getContext()
do {
try context.save()
} catch {
print("error during the save.")
}
}
这里是实体对象的两个classes的代码:
// DataClass
@objc(ScoreSheet)
public class ScoreSheet: NSManagedObject {
static var supportsSecureCoding: Bool {
return true
}
}
//DataProperties
extension ScoreSheet {
@nonobjc public class func fetchRequest() -> NSFetchRequest<ScoreSheet> {
return NSFetchRequest<ScoreSheet>(entityName: "ScoreSheet")
}
@NSManaged public var name: String
@NSManaged public var score: [Measure]
}
Clearly Measure class 实现 NSSecureCoding 和解码和编码对象的方法。
这是措施 class 实施:
import Foundation
class Measure: NSObject, NSCoding, NSSecureCoding {
var elements : [ScoreElement] = []
var timeSig : TimeSignature
var clef : Clef
static var supportsSecureCoding = true
init(time : TimeSignature, clef : Clef) {
self.timeSig = time
self.clef = clef
}
func encode(with encoder: NSCoder) {
encoder.encode(self.elements, forKey: "elements")
encoder.encode(self.timeSig, forKey: "timeSig")
encoder.encode(self.clef, forKey: "clef")
}
required convenience init? (coder decoder: NSCoder) {
let elements = decoder.decodeObject(forKey: "elements") as! [ScoreElement]
let timeSig = decoder.decodeObject(forKey: "timeSig") as! TimeSignature
let clef = decoder.decodeObject(forKey: "clef") as! Clef
self.init(time: timeSig, clef: clef)
self.elements = elements
}
}
我不确定出了什么问题,但有几处问题需要修复,可能会影响您的结果。
首先,计算出的转换器名称与您尝试使用的转换器名称不同。当这一行执行时,T
是 Measure
,
let className = NSStringFromClass(T.self)
然后 className
会变成 MyProjectName.Measure
。计算出的转换器名称最终类似于 NSValueTransformerName(_rawValue: DHCMyProjectName.MeasureValueTransformer)
,这与您在数据模型中使用的名称不匹配。所有这些都意味着您的变压器未被使用。
但这可能并不重要,因为如果 Measure
符合 NSSecureCoding
和 Measure
的所有属性(ScoreElement
、TimeSignature
、 Clef
) 也符合NSSecureCoding
(这似乎是因为你的代码没有抛出异常),然后你不根本不需要自定义转换器。如果一个可转换的 属性 类型符合 NSSecureCoding
那么 Core Data 将自动使用 NSSecureCoding
。除非出于某种原因不想或不能符合 NSSecureCoding
,否则您不需要自定义转换器。因此,您的变压器未被使用并不重要。
至于为什么 Measure
没有在 encode/decode 过程中幸存下来,我不知道,但你可以通过消除不必要的 encode/decode 的干扰来帮助解决问题class。我还建议在 encode(with:)
和 init(coder:)
方法中的 Measure
中放置一个断点。您应该在保存和获取数据时遇到这些断点。