核心数据和Xcode 11:请切换到使用“NSSecureUnarchiveFromData”或“NSSecureUnarchiveFromDataTransformer”的子类。

34

刚升级到Xcode 11,启动时出现以下崩溃:

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

CoreData: warning: 实体“Group”上的属性“color”正在使用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中正常运行。


请仔细阅读错误提示,它非常清晰。但是为什么要同时保存颜色和十六进制字符串表示?后者已经足够了。 - vadian
我想继续使用 nil,因为它一直运行良好。我需要在每个记录中保存十六进制和UIColor属性,以便我不必来回转换。 - user1107173
不需要保存两者。计算属性提供了转换并且保存两者会更加昂贵。否则请按照错误提示进行操作。 - vadian
1
我希望能得到一些关于创建“Transformer”的帮助。我想我必须自己解决问题并在这里发布解决方案了。 - user1107173
不是真的。我绕过了它,使用了hexColor: String?而不是存储UIColor - user1107173
显示剩余2条评论
8个回答

37

Swift 5.4.2

这对我有用。

编辑 文章链接在这里

  • 在项目导航器中点击.xcdatamodeld文件
  • 点击具有Transformable属性的实体
  • 点击Transformable属性
  • 点击“显示数据模型检查器”图标
  • 在转换器字段中输入'NSSecureUnarchiveFromDataTransformer' - 编辑 将其更改为'SecureUnarchiveFromData' 自Swift 5.5.2起。

editor_orientation_preview

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

3
点击“显示数据模型检查器”图标。我找不到这个图标... 更新:我找到了,我必须重新启动Xcode,然后在右侧边栏中,带有此图标的选项卡是其中之一。如果它显示“无选择”,请重新启动Xcode,在我的情况下解决了这个问题。 - charelf

28

将Transformer属性设置为NSSecureUnarchiveFromDataTransformer可以解决我的问题。要实现此目标,请选择属性并将其转换器类型设置为NSSecureUnarchiveFromDataTransformer,然后按command+R再次运行即可。

谢谢, Ratneshwar


这对于 UIColor 不起作用,因为它不受 NSSecureUnarchiveFromDataTransformer 的支持。 - Koraktor
3
iOS 11怎么样了?'NSSecureUnarchiveFromDataTransformer'仅适用于iOS 12.0或更新版本。 - Maetschl

13

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

我的经验是尝试定义一个属性来持久化采用NSCoding的自定义类。最初我遇到了一种类似于问题提出者所提到的错误提示,我能够通过将属性上的转换器字段更改为"NSSecureUnarchiveFromData"来抑制警告,但接着我又收到了一个错误,大致如下:Not able to save to CoreData. SQLCore dispatchRequest Object of class “ ” not among allowed top level class list... ,如此提到here。在我的情况下,将属性更改为关系是不可取的建议。

进一步研究得到了这篇博客文章,其中详细解释了所有这些的“原因”,并给出了对我有效的解决方案。这篇博客实际上在示例中使用了UIColor的情况,但对于任何自定义类都适用。

假设你有一个想要存储为某个实体中Transformable属性的自定义类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)
    }
}

然后请确保将属性中的转换器字段设置为"CustomClassValueTransformer",将自定义类字段设置为"CustomClass",这样您就可以开始使用了。


3
对于可转换属性,您需要在“自定义类”字段中设置其类型。
例如,我有一个可转换的字段,它存储了一个数字数组,并声明其自定义类为[Int16]。这很可能是导致崩溃的原因。正如@vadian之前提到的,您不需要两个字段。
在修复崩溃后,您可以通过将Transformer字段设置为NSSecureUnarchiveFromData来消除警告(只需在字段中键入此内容即可)。

2
我无法让这个解决方案工作!将NSSecureUnarchiveFromData添加到转换器字段会导致项目崩溃。至于更改自定义类字段,这也会导致崩溃。 - hoboBob
新名称实际上是“NSSecureUnarchiveFromDataTransformer”,所以您只是缺少末尾的“Transformer”一词 :) - Bob de Graaf

3

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

  • First add a new subclass of NSSecureUnarchiveFromDataTransformer

    @interface ColorValueTransformer : NSSecureUnarchiveFromDataTransformer

  • Add the following static method to your implementation file:

    @implementation ColorValueTransformer
    
    + (NSArray<Class> *)allowedTopLevelClasses {
              return @[UIColor.class]; 
    }
    
    @end
    
  • Open your data model (e.g. datamodel..xcdatamodeld)

  • Select the entity and the related attribute which needs the new Transformer

  • Open the Data Model Inspector

  • Add the class name (e.g. ColorValueTransformer) as Transformer to that attribute

  • Change the Custom Class to UIColor

  • Build and run…


无法构建此内容。将自定义类字段设置为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`
- mretondo
太棒了。我一直在更新一个非常老的Core Data应用程序,这不是我编写的,这是我最后的真正障碍。这个完美地解决了问题。谢谢。 - HangarRash

1

I received the same warning messages when updating to Xcode 11, however in my case they are just warnings, but no crash.

In order to work out the best solution, I tried creating a stripped down sample app with just a single entity containing a transformable attribute. But it seems that no matter what I tried I could not reproduce the problem. The I copied the model file from my main app to the demo app, and of course that failed.

So I got to the point where I just had two model files and a simple unit test which does nothing more than open the model and create a persistent store container:

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

The appmodel causes the error messsages but the sample model does not. Even when I stripped the appmodel down to a single Entity it continues to generate the errors.

Comparing the contents of the samplemodel with the appmodel (show package contents in Finder), there is a hidden file called .xccurrentversion in the samplemodel but not in the appmodel. The file looks like this:

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

So I created a similar file for the appmodel and put it in the package folder. Surprisingly, that silences the warning messages! Conversely, deleting the .xccurrentversion file from the samplemodel causes the error messages to be generated. This will allow testing of the problem in isolation.

So this may be a short term fix. In the meantime, I need to work out how to migrate to secure coding.


0

也许这些答案中有一些是可以接受的,但在我的情况下,我去了this帖子。我希望它能够提供很大的帮助。


0
小心!!!
我可以确认以下两个方法:
- NSSecureUnarchiveFromData - NSSecureUnarchiveFromDataTransformer 会破坏iCloud同步⚠️⚠️。请不要使用它们。
在“Value Transformer Name”字段中保持为空(检查器),并保持警告。

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