结合my previous questions,我决定继承NSArrayController
的子类,以实现所需的行为。
class NSPresetArrayController: NSArrayController {
override func addObject(_ object: Any) {
if let preset = object as? Preset {
super.addObject(["name": preset.name, "value": preset.value])
} else {
super.addObject(object)
}
}
}
这行得通,但是如果我想要一种对任何Encodable
类都适用的东西,而不仅仅是具有两个名为name
和value
的属性的东西怎么办?
基本上,问题是从类创建字典,其中键是属性名称,值是这些属性的值。
我试图写这样的东西:
class NSPresetArrayController: NSArrayController {
override func addObject(_ object: Any) {
if let encodableObject = object as? Encodable {
let data = try! PropertyListEncoder().encode(encodableObject)
let any = try! PropertyListSerialization.propertyList(from: data, options: [], format: nil)
super.addObject(any)
}
}
}
但是,出现编译错误:
Cannot invoke 'encode' with an argument list of type '(Encodable)'
1. Expected an argument list of type '(Value)'
如何解决此问题以使其编译?
答案 0 :(得分:2)
问题是protocols don't always conform to themselves。 PropertyListEncoder
的{{3}}方法需要一个Value : Encodable
参数:
func encode<Value : Encodable>(_ value: Value) throws -> Data
但是Encodable
类型本身目前无法满足此约束(但在将来的语言版本中可能会做得到)。
如在链接的“问答”(和encode(_:)
中所探讨的)一样,解决此限制的一种方法是 open 打开Encodable
值以挖掘底层的具体类型,我们可以代替Value
。我们可以通过协议扩展来做到这一点,并使用包装器类型来封装它:
extension Encodable {
fileprivate func openedEncode(to container: inout SingleValueEncodingContainer) throws {
try container.encode(self)
}
}
struct AnyEncodable : Encodable {
var value: Encodable
init(_ value: Encodable) {
self.value = value
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try value.openedEncode(to: &container)
}
}
应用于您的示例:
class NSPresetArrayController : NSArrayController {
override func addObject(_ object: Any) {
guard let object = object as? Encodable else {
// Not encodable, maybe do some error handling.
return
}
do {
let encoded = try PropertyListEncoder().encode(AnyEncodable(object))
let cocoaPropertyList = try PropertyListSerialization.propertyList(from: encoded, format: nil)
super.addObject(cocoaPropertyList)
} catch {
// Couldn't encode. Do some error handling.
}
}
}
答案 1 :(得分:0)
您传递给encode(_:)
的值的类型必须是实现Encodable
的具体类型。这意味着您需要从拥有的Any
中恢复对象的实型。为了进行转换,您必须具有要静态转换的静态类型。换句话说,您不能说object as! type(of: object)
;您必须说object as? MyClass
(或者在一般情况下您可以说object as? T
)。
因此,我相信解决此问题的唯一方法是静态枚举正在使用的类型,例如:
import Foundation
struct S : Encodable {
let i: Int
}
struct T : Encodable {
let f: Float
}
struct U : Encodable {
let b: Bool
}
func plistObject(from encodable: Any) -> Any? {
let encoded: Data?
switch encodable {
case let s as S:
encoded = try? PropertyListEncoder().encode(s)
case let t as T:
encoded = try? PropertyListEncoder().encode(t)
case let u as U:
encoded = try? PropertyListEncoder().encode(u)
default:
encoded = nil
}
guard let data = encoded else { return nil }
return try? PropertyListSerialization.propertyList(from: data,
options: [],
format: nil)
}
毋庸置疑,这相当荒谬。它是不灵活的,重复的样板。我不确定我是否可以推荐使用它。这是字面问题的答案,不一定是问题的解决方案。