【问题标题】:Error with retrieve / save custom object in core data在核心数据中检索/保存自定义对象时出错
【发布时间】:2020-12-22 21:55:07
【问题描述】:

我开发了一个 ios 应用程序,允许用户编辑乐谱表,现在我想实现数据持久性以防止丢弃更改。

阅读 ios 文档后,我注意到存在改善数据持久性的不同方法,我相信对我的应用程序来说最好的方法是 Core Data。 考虑到我的应用程序使用了很多自定义对象,我遇到了很多问题。

我正在尝试使用核心数据来保存一个实体,称为分数表,由两个属性组成:

  • 名称:字符串
  • score:另一个自定义对象(Measure)的数组,由其他自定义对象(Score Element)组成

根据文档和其他 q/a,我决定在模型上使用 Trasformable 类型:

所以我声明了一个通用类,用作 score 属性的转换器:

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 文件中的转换器。 问题是,当我保存时,没有发生错误,但是当我为重新启动应用程序获取数据时,我可以只获取分数表的名称,而分数数组是空的,就像没有元素放在里面一样(显然,在保存之前,我尝试打印数组内容,这证明我正在使用非空数组)

这是保存的代码:

static func saveContext() {
        let context = getContext()
        do {
            try context.save()
        } catch {
            print("error during the save.")
        }
    }

这里是实体对象的两个类的代码:


// 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 类实现了 NSSecureCoding 和对对象进行解码和编码的方法。

这里是 Measure 类的实现:


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
    }
    
  
}

【问题讨论】:

  • 答案将取决于Measure 类的实现方式。到目前为止,您包含的代码并未显示 Measure 实现了该协议。
  • 我已经编辑了问题并添加了 Measure 类

标签: ios swift core-data swift5


【解决方案1】:

我不确定出了什么问题,但有几处需要修复可能会影响您的结果。

首先,计算出的转换器名称与您尝试使用的名称不同。当这一行执行时,TMeasure

let className = NSStringFromClass(T.self)

然后className 将类似于MyProjectName.Measure。计算出的转换器名称最终类似于NSValueTransformerName(_rawValue: DHCMyProjectName.MeasureValueTransformer),与您在数据模型中使用的名称不匹配。所有这些都意味着你的变压器没有被使用。

但这可能无关紧要,因为如果Measure 符合NSSecureCodingMeasure 的所有属性(ScoreElementTimeSignatureClef符合NSSecureCoding(这似乎是因为你的代码没有抛出异常),那么你根本不需要自定义转换器。如果可转换的属性类型符合NSSecureCoding,那么Core Data 将自动使用NSSecureCoding。您不需要自定义转换器,除非您出于某种原因不想或不能符合NSSecureCoding。因此,您的变压器没有被使用并不重要。

至于为什么Measure 无法在编码/解码过程中幸存下来,我不知道,但您可以通过消除不必要的编码/解码类的干扰来帮助解决问题。我还建议在encode(with:)init(coder:) 方法中的Measure 中放置一个断点。您应该在保存和获取数据时遇到这些断点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-18
    • 2011-03-15
    相关资源
    最近更新 更多