关于这个主题有很多问题,但我还没有弄清楚为什么我的解决方案还不起作用。
我有一些规定
protocol Foo: Decodable {
var prop1: String? { get set }
var prop2: Bool? { get set }
var prop3: Int? { get set }
init()
}
enum FooCodingKeys: CodingKey { case prop1, prop2, prop3 }
extension Foo {
init(from decoder: Decoder) throws {
self.init()
let container = try decoder.container(keyedBy: FooCodingKeys.self)
self.prop1 = try container.decode(String?, forKey: .prop1)
self.prop2 = try container.decode(Bool?, forKey: .prop2)
self.prop3 = try container.decode(Int?, forKey: .prop3)
}
}
从技术上讲,它有一个默认的实现,可以使整个协议完全可解码,编译器一点也不抱怨,现在在一个结构体中,如果我有
enum BarCodingKeys: CodingKey { case foos }
struct Bar: Decodable {
var foos: [Foo]
init(from decoder: Decoder) {
let container = try decoder.container(keyedBy: BarCodingKeys.self)
self.foos = try container.decode([Foo].self, forKey: .prop1)
}
}
然后得到错误Protocol 'Foo' as a type cannot conform to 'Decodable'
。
有没有一种方法可以让协议符合使用扩展的可编码性?如果没有,为什么?
2条答案
按热度按时间ss2ws0br1#
我不确定你的用例是什么,所以我的建议可能不适合,但最简单的方法是告诉编译器你正在解码一个具体的类型,而不是一个协议,但这个具体的类型实现了
Foo
,所以你这样修改Bar
:所以现在你正在解码
[T].self
-一个具体的类型,而不是协议。当然,缺点是你必须在解码类本身时提供一个类型,也就是说,你不能说:您必须在此处提供类型:
但是,如您所见,Foo1不需要实现
init(from decoder: Decoder) throws
,正确使用了协议中的init(from decoder: Decoder) throws
。补充说明:正如你可能注意到的,我把
forKey: .prop1
改成了forKey: .foos
,因为根据你的代码,你期望一个具有foos
属性的对象,它作为一个值包含一个对象数组,这些对象与Foo
协议匹配,如下所示:如果不是这种情况,请提供一个你正在尝试解码的JSON的例子。并且你还需要修复这个函数(使用
decodeIfPresent
代替可选):4uqofj5v2#
首先,我认为这里有一个关键的误解:
在你的主题中,你建议这是“使协议符合Codable”,但这不是它的作用,它说“为了符合Foo,一个类型必须首先符合Decodable”,Foo本身绝对不符合Codable,然后你注意到:
从技术上讲,这现在有一个默认的实现,应该使整个协议完全解码。
这在任何一个方向上都是不正确的。考虑一个简单的例子:
你的
init(from:)
是如何解码ConcreteFoo的?anotherPropNotInFoo
的值是多少?在另一个方向上,考虑三种符合类型:
假设您的JSON为:
现在考虑以下代码:
应该发生什么?它们应该是什么实际类型?记住,可能存在无限数量的其他Foo实现(包括在其他模块中)。这是行不通的。如果你的意思是Foo只有这些属性,没有其他属性,也没有其他方法,那么它就不是一个协议。它只是一个结构。如果你的意思是有无限多的类型实现它,那就无法确定它们是什么。