在采用NSSecureunarchiveFromDatatRansformer作为可转换属性时崩溃



在iOS 12中,Apple引入了NSSecureUnarchiveFromDataTransformerName,用于Coredata模型实体可转换属性。我曾经将变压器名称字段保持为空,该字段隐式使用了NSKeyedUnarchiveFromDataTransformerName。现在对该变压器进行了弃用,将来将场空白的代替意味着NSSecureUnarchiveFromDataTransformerName

在iOS 13中,如果该字段是空的,则您现在会收到一个运行时警告,告诉您上述内容。我在任何地方都找不到任何文档,我获得的唯一参考是WWDC 2018核心数据最佳实践谈话,简短地提到了我刚才所说的。

现在,我有一个模型,该模型直接将HTTPURLResponse对象直接存储在可转换属性中。它符合NSSecureCoding,我在运行时检查了supportsSecureCodingtrue

设置Transformer名称的NSSecureUnarchiveFromDataTransformerName与此消息崩溃:

Object of class NSHTTPURLResponse is not among allowed top level class list (
    NSArray,
    NSDictionary,
    NSSet,
    NSString,
    NSNumber,
    NSDate,
    NSData,
    NSURL,
    NSUUID,
    NSNull
) with userInfo of (null)

因此,听起来像是可转换的属性只能是这些顶级对象。

我尝试将安全变压器分类并覆盖allowedTopLevelClasses属性,如文档所建议:

@available(iOS 12.0, *)
public class NSSecureUnarchiveHTTPURLResponseFromDataTransformer: NSSecureUnarchiveFromDataTransformer {
    override public class var allowedTopLevelClasses: [AnyClass] {
        return [HTTPURLResponse.self]
    }
}

然后我想我可以创建一个自定义变压器名称,将其设置在模型中,并为该名称调用setValueTransformer(_:forName:),但是我找不到API来为我的自定义名称设置默认的NSKeyedUnarchiveFromDataTransformer,以防万一我在iOS 11。

请记住,我正在使用Xcode 11 beta 5,但是如果我接受我所说的错误的含义,这似乎没有关系。

感谢任何想法。

我编写了一个简单的模板类,该类使得为实现NSSecureCoding的任何类创建和注册一个变压器。至少在使用UIColor作为可转换属性的简单测试中,它对我的效果很好。

使用它(以UIColor为例(:

// Make UIColor adopt ValueTransforming
extension UIColor: ValueTransforming {
  static var valueTransformerName: NSValueTransformerName { 
    .init("UIColorValueTransformer")
  }
}
// Register the transformer somewhere early in app startup.
NSSecureCodingValueTransformer<UIColor>.registerTransformer()

在核心数据模型中使用的变压器的名称是UIColorValueTransformer

import Foundation
public protocol ValueTransforming: NSSecureCoding {
  static var valueTransformerName: NSValueTransformerName { get }
}
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
  }
  /// Registers the transformer by calling `ValueTransformer.setValueTransformer(_:forName:)`.
  public static func registerTransformer() {
    let transformer = NSSecureCodingValueTransformer<T>()
    ValueTransformer.setValueTransformer(transformer, forName: T.valueTransformerName)
  }
}

我也尝试使用NSSecureUnarchiveFromDataTransformer(尽管我不需要安全的编码,请参见下文(,但我没有成功。因此,我改用自定义值变压器。我的步骤是:

我实现了我的自定义值变压器类:

@objc(MyTransformer)
class MyTransformer: ValueTransformer {
    override class func setValueTransformer(_ transformer: ValueTransformer?, forName name: NSValueTransformerName) {
        ValueTransformer.setValueTransformer(transformer, forName: name)
    }
    override func transformedValue(_ value: Any?) -> Any? {
        guard let value = value else { return nil }
        let data = serialize(value) // A custom function, e.g. using an NSKeyedArchiver
        return data as NSData
    }
    override class func allowsReverseTransformation() -> Bool {
        return true
    }
    override func reverseTransformedValue(_ value: Any?) -> Any? {
        guard let value = value else { return nil }
        guard let data = value as? Data else { return nil }
        let set = deserialize(data) // A custom function, e.g. using an NSKeyedUnarchiver
        return set as NSSet // Or as an NSArray, or whatever the app expects
    }
}
extension NSValueTransformerName {
    static let myTransformerName = NSValueTransformerName(rawValue: „MyTransformer")
}  

需要第一行(@objc(,请参阅此帖子!否则,Coredata将无法识别自定义变压器!

接下来,我在应用程序委托中实现了一个计算的属性,根据这篇文章:

private let transformer: Void = {
    MyTransformer.setValueTransformer(MyTransformer(), forName: .myTransformerName)
}()  

重要的是要尽早这样做,例如在应用程序委托中,因此Coredata在初始化时确实识别了变压器。

最终,我将xcdatamodeld中可转换属性的属性检查器设置为MyTransformer的变压器值。

然后代码在没有运行时间日志的情况下正确运行。
请注意:就我而言,不必进行安全编码,但是可以轻松修改上述代码以改用安全编码。只需相应地修改函数serializedeserialize即可。

编辑(由于下面的Kas-Kad的评论(:

对不起,不幸的是,我的代码还没有完成。

在应用程序委托中,我使用了以下计算属性(请参阅此链接(。这样可以确保即使在运行init之前,值得早就注册了值。

private let transformer : Void = {
    let myTransformer = MyValueTransformer()
    ValueTransformer.setValueTransformer(myTransformer, forName:NSValueTransformerName("MyValueTransformer"))
}()  

override class func setValueTransformer在我的实现中确实没有。我从某个地方复制了它(不记得(。因此,人们肯定会省略它。

NSValueTransformerName的扩展只能使用.myTransformerName作为变压器名称。

相关内容

  • 没有找到相关文章

最新更新