在iOS 12中,Apple引入了NSSecureUnarchiveFromDataTransformerName
,用于Coredata模型实体可转换属性。我曾经将变压器名称字段保持为空,该字段隐式使用了NSKeyedUnarchiveFromDataTransformerName
。现在对该变压器进行了弃用,将来将场空白的代替意味着NSSecureUnarchiveFromDataTransformerName
。
在iOS 13中,如果该字段是空的,则您现在会收到一个运行时警告,告诉您上述内容。我在任何地方都找不到任何文档,我获得的唯一参考是WWDC 2018核心数据最佳实践谈话,简短地提到了我刚才所说的。
现在,我有一个模型,该模型直接将HTTPURLResponse
对象直接存储在可转换属性中。它符合NSSecureCoding
,我在运行时检查了supportsSecureCoding
是true
。
设置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
的变压器值。
然后代码在没有运行时间日志的情况下正确运行。
请注意:就我而言,不必进行安全编码,但是可以轻松修改上述代码以改用安全编码。只需相应地修改函数serialize
和deserialize
即可。
编辑(由于下面的Kas-Kad的评论(:
对不起,不幸的是,我的代码还没有完成。
在应用程序委托中,我使用了以下计算属性(请参阅此链接(。这样可以确保即使在运行init
之前,值得早就注册了值。
private let transformer : Void = {
let myTransformer = MyValueTransformer()
ValueTransformer.setValueTransformer(myTransformer, forName:NSValueTransformerName("MyValueTransformer"))
}()
和 override class func setValueTransformer
在我的实现中确实没有。我从某个地方复制了它(不记得(。因此,人们肯定会省略它。
NSValueTransformerName
的扩展只能使用.myTransformerName
作为变压器名称。