【问题标题】:Core Data & Xcode 11: Please switch to using "NSSecureUnarchiveFromData" or a subclass of NSSecureUnarchiveFromDataTransformerCore Data & Xcode 11:请切换到使用“NSSecureUnarchiveFromData”或 NSSecureUnarchiveFromDataTransformer 的子类
【发布时间】:2020-01-22 23:41:58
【问题描述】:

刚迁移到 Xcode 11 并在启动时遇到以下崩溃:

CoreData:故障:此应用程序中的一个或多个模型正在使用未设置或设置为 NSKeyedUnarchiveFromDataTransformerName 的转换器名称的可转换属性。请改用“NSSecureUnarchiveFromData”或 NSSecureUnarchiveFromDataTransformer 的子类。在某些时候,当指定 nil 时,Core Data 将默认使用“NSSecureUnarchiveFromData”,并且包含不支持 NSSecureCoding 的类的可转换属性将变得不可读。

CoreData:警告:实体“组”上的属性“颜色”正在使用 nil 或不安全的 NSValueTransformer。请改用“NSSecureUnarchiveFromData”或 NSSecureUnarchiveFromDataTransformer 的子类。

我正在使用以下代码在启动时创建NSPersistentContainer

private let container: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "MyApp", managedObjectModel: MyAppModelVersion.current.managedObjectModel())
    let storeDescription = NSPersistentStoreDescription(url: getStoreURLWithUserName())
    storeDescription.shouldMigrateStoreAutomatically = true
    storeDescription.shouldInferMappingModelAutomatically = true
    container.persistentStoreDescriptions = [storeDescription]
    return container
}()

执行此行后立即发生错误:

let container = NSPersistentContainer(name: "MyApp", managedObjectModel: MyAppModelVersion.current.managedObjectModel())

我还有一个名为“Colorin aGroup”的可变形实体:

@NSManaged public var color: UIColor?
@NSManaged public var hexColorValue: String?

以下是如何设置属性:

public var hexColor: String? {
    get {
        return self.hexColorValue
    }
    set {
        self.hexColorValue = newValue
        if let str = newValue {
            self.color = UIColor(hex: str)
        }
    }
}

这是该属性在 Core Data 中的样子:

我不确定如何从这次崩溃中恢复。这在 Xcode 10 上运行良好

【问题讨论】:

  • 请仔细阅读错误,非常清楚。但是为什么要同时保存颜色和十六进制字符串表示?后者就足够了。
  • 我想继续使用nil,因为它一直运行良好。我需要为每条记录保存十六进制和 UIColor 属性,这样我就不必来回转换。
  • 您确实不需要两者都需要保存。计算属性提供了转换,而保存两者的成本要高得多。否则按照错误提示执行。
  • 谢谢。有关如何通过该错误的任何建议?
  • 我希望在创建Transformer 方面获得一些帮助。我想我必须弄清楚并在这里发布解决方案。

标签: swift xcode core-data core-data-migration


【解决方案1】:

将 Transformer 属性设置为 NSSecureUnarchiveFromDataTransformer 解决了我的警告。为此选择属性并将其转换器类型设置为 NSSecureUnarchiveFromDataTransformer 并按 commond+R 再次运行。

谢谢, 拉特内什瓦尔

【讨论】:

  • 这不适用于UIColor,因为它不受NSSecureUnarchiveFromDataTransformer 的支持。
  • iOS 11 怎么样? NSSecureUnarchiveFromDataTransformer' 仅适用于 iOS 12.0 或更高版本
【解决方案2】:

这与从 NSCoding 迁移到 NSSecureCoding 协议有关。默认的ValueTransformer 采用NSCoding,所以唯一对我有用的解决方案是编写我自己的采用NSSecureUnarchiveFromDataTransformer 协议的Transformer

我应该说我自己的经验是尝试用 Transformer 类型定义一个 Attribute 来持久化一个采用 NSCoding 的自定义类。我最初遇到了类似于 OP 的 错误警告。正如其他人所提到的,我可以通过将属性上的 Transformer 字段更改为“NSSecureUnarchiveFromData”来抑制警告,但随后我收到了如下错误: Not able to save to CoreData. SQLCore dispatchRequest Object of class “ ” not among allowed top level class list... 如上所述 here。在我的情况下,将属性更改为关系的建议是不可取的。

更多挖掘提出了this blog post,详细说明了所有这些的“原因”,并提供了一个对我有用的解决方案。该博客在示例中实际上使用了UIColor 的情况,但它也适用于任何自定义类。

假设您有一个CustomClass,您想将其作为可转换属性存储在某个实体中。如果您像我一样,您可能已经采用NSCoding 并收到上述错误。解决方案是改用NSSecureCoding 并定义一个NSSecureUnarchiveFromDataTransformer 子类:

@objc(CustomClassValueTransformer)
final class CustomClassValueTransformer: NSSecureUnarchiveFromDataTransformer {

    static let name = NSValueTransformerName(rawValue: String(describing: CustomClass.self))

    // Make sure `CustomClass` is in the allowed class list,
    // AND any other classes that are encoded in `CustomClass`
    override static var allowedTopLevelClasses: [AnyClass] {
        // for example... yours may look different
        return [CustomClass.self, OtherClass.self, NSArray.self, NSValue.self]
    }

    /// Registers the transformer.
    public static func register() {
        let transformer = CustomClassValueTransformer()
        ValueTransformer.setValueTransformer(transformer, forName: name)
    }
}

然后确保将 Attribute 上的 Transformer 字段设置为“CustomClassValueTransformer”,将 Custom Class 字段设置为“CustomClass”,这样就可以了。

【讨论】:

    【解决方案3】:

    Swift 5.4.2

    这对我有用。

    编辑文章链接为here

    • 在项目导航器中单击 .xcdatamodeld 文件
    • 点击 具有可变形属性的实体
    • 单击可转换属性
    • 点击“显示数据模型检查器”图标
    • 输入“NSSecureUnarchiveFromDataTransformer” 在变压器领域

    您的警告/错误应该消失。如果没有,请尝试清理您的构建文件夹并重新构建。

    【讨论】:

    • Click the 'Show Data Model Inspector' icon 我找不到这个图标.. 更新:我找到了,我必须重新启动 Xcode,然后在右侧边栏中,带有this icon 的选项卡就是一个。如果它说no selection,重启Xcode,在我的情况下解决了。
    【解决方案4】:

    对于 Objektive-C 和 iOS 14,以下解决方案适用于 UIColor 属性。

    • 首先添加NSSecureUnarchiveFromDataTransformer的新子类

    @interface ColorValueTransformer : NSSecureUnarchiveFromDataTransformer

    • 将以下静态方法添加到您的实现文件中:

      @implementation ColorValueTransformer

      + (NSArray<Class> *)allowedTopLevelClasses {

              return @[UIColor.class]; 
      

      }

      @end

    • 打开您的数据模型(例如 datamodel..xcdatamodeld)

    • 选择需要新建Transformer的实体及相关属性

    • 打开数据模型检查器

    • 将类名(例如ColorValueTransformer)作为Transformer添加到该属性

    • 自定义类更改为UIColor

    • 构建并运行……

    【讨论】:

    • 无法构建它。将自定义类字段设置为 UIColor 会导致自动生成的文件将变量设置为类型 UIColor 但文件不导入 UIKit 所以我在下面文本的最后一行收到此错误。 // This file was automatically generated and should not be edited. import Foundation import CoreData. extension Themes {. @nonobjc public class func fetchRequest() -> NSFetchRequest<Themes> {. return NSFetchRequest<Themes>(entityName: "Themes"). }. @NSManaged public var backgroundColor: UIColor? //Cannot find type 'UIColor' in scope
    【解决方案5】:

    对于可转换属性,您需要在自定义类字段中设置其类型。

    例如,我有一个存储数字数组的可转换字段,它的自定义类被声明为[Int16]。这很可能是坠机的原因。正如@vadian 之前提到的,您不需要这两个字段。

    修复崩溃后,您可以通过将 Transformer 字段设置为 NSSecureUnarchiveFromData 来消除警告(您只需在该字段中输入即可)

    【讨论】:

    • 我无法使用此解决方案!将 NSSecureUnarchiveFromData 添加到变压器字段会导致项目崩溃。至于更改自定义类字段。这也会导致崩溃
    • 新名称实际上是“NSSecureUnarchiveFromDataTransformer”,所以您只是在末尾缺少“Transformer”这个词 :)
    【解决方案6】:

    更新到 Xcode 11 时,我收到了相同的警告消息,但在我的情况下,它们只是警告,而不是崩溃。

    为了找到最佳解决方案,我尝试创建一个精简的示例应用程序,其中只有一个包含可转换属性的实体。但似乎无论我尝试什么,我都无法重现该问题。我将模型文件从我的主应用程序复制到演示应用程序,当然失败了。

    所以我到了只有两个模型文件和一个简单的单元测试的地步,它只是打开模型并创建一个持久存储容器:

     func testDataModels() {
         openDataModel(named: "samplemodel")
         openDataModel(named: "appmodel")
     }
    
     func openDataModel(named name: String) {
        print("Opening \(name)")
         guard let url = findFile(forResource: name, withExtension: "momd"),
               let managedObjectModel = NSManagedObjectModel(contentsOf: url)
         else {
             XCTFail("Unable to find \(name) data model")
             return
         }
        print(url)
         _ = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
     }
    
     func findFile(forResource name: String, withExtension ext: String) -> URL? {
         if let url = Bundle(for: type(of: self)).url(forResource: name, withExtension: ext) {
             return url
         }
         return Bundle.main.url(forResource: name, withExtension: ext)
     }
    

    appmodel 会导致错误消息,但示例模型不会。即使我将 appmodel 剥离为单个实体,它也会继续生成错误。

    比较 samplemodel 和 appmodel 的内容(在 Finder 中显示包内容),在 samplemodel 中有一个名为 .xccurrentversion 的隐藏文件,但在 appmodel 中没有。该文件如下所示:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>_XCCurrentVersionName</key>
        <string>samplemodel.xcdatamodel</string>
    </dict>
    </plist>
    

    所以我为 appmodel 创建了一个类似的文件并将它放在包文件夹中。 令人惊讶的是,这会使警告消息静音!相反,从 samplemodel 中删除 .xccurrentversion 文件会导致生成错误消息。这将允许单独测试问题。

    所以这可能是一个短期修复。与此同时,我需要弄清楚如何迁移到安全编码。

    【讨论】:

      【解决方案7】:

      也许其中一些答案是可以接受的,就我而言,我去了this 帖子。我希望它会有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多