可编码的结构体包含一个协议属性。

10

我有一个继承自Codable的协议。

protocol OrderItem:Codable {
    var amount:Int{get set}
    var isPaid:Bool{get set}
} 

并且一个结构体符合这个协议

struct ProductItem:OrderItem {
    var amount = 0
    var isPaid = false
    var price = 0.0
}

然而,当我将这个结构放入可编码的结构中时,出现了错误。
struct Order:Codable {
    var id:String
    var sn:String = ""
    var items:[OrderItem] = []
    var createdAt:Int64 = 0
    var updatedAt:Int64 = 0
}

这些错误是

Type 'Order' does not conform to protocol 'Encodable'
Type 'Order' does not conform to protocol 'Decodable'

但是,如果我将items:[OrderItem]更改为items:[ProductItem],则一切正常!

我该如何解决这个问题?

3个回答

15
由于协议只规定了你必须做什么,所以当你将协议X适配到Codable时,这意味着任何符合协议X的类型也必须符合Codable,但它并不会提供所需的实现。你可能感到困惑是因为Codable不需要你在所有类型已经符合Codable时去实现任何东西。如果Codable要求你实现一个名为myFunction的函数,那么你的OrderItem将缺少该函数的实现,并且编译器会提示你添加它。
以下是你可以采取的替代方法:
struct Order<T: OrderItem>: Codable {
   var id:String
   var sn:String = ""
   var items: [T] = []
   var createdAt:Int64 = 0
   var updatedAt:Int64 = 0
}

您现在说items是符合OrderItem的通用类型。


太棒了!非常感谢你! - Tim

5
值得一提的是,如果您有一个数组属性并且类型是协议:let arrayProtocol: [MyProtocol],并且该数组包含多个符合MyProtocol的类型,那么您将不得不实现自己的init(from decoder: Decoder) throws来获取值,并且实现func encode(to encoder: Encoder) throws来编码它们。
例如:
protocol MyProtocol {}
struct FirstType: MyProtocol {}
struct SecondType: MyProtocol {}

struct CustomObject: Codable {
   let arrayProtocol: [MyProtocol]

   enum CodingKeys: String, CodingKey {
      case firstTypeKey
      case secondTypeKey
   }
}

所以我们的解码将如下所示:
init(from decoder: Decoder) throws {
   let values = try decoder.container(keyedBy: CodingKeys.self)
   // FirstType conforms to MyProtocol
   let firstTypeArray = try values.decode([FirstType].self, forKey: .firstTypeKey)
   // SecondType conforms to MyProtocol
   let secondTypeArray = try values.decode([SecondType].self, forKey: .secondTypeKey)
   // Our array is finally decoded
   self.arrayProtocol: [MyProtocol] = firstTypeArray + secondTypeArray
}

对于编码,我们需要在进行编码之前将其转换为实际类型:

func encode(to encoder: Encoder) throws {
   var container = encoder.container(keyedBy: CodingKeys.self)
   let firstActualTypeArray = arrayProtocol.compactMap{$0 as? FirstType}
   let secondActualTypeArray = arrayProtocol.compactMap{$0 as? SecondType}

   try container.encode(firstActualTypeArray, forKey: .firstTypeKey)
   try container.encode(secondActualTypeArray, forKey: .secondTypeKey)
}

我可能对try的工作原理感到困惑,如果任何一个try语句引发错误,那么初始化器不会立即引发错误吗?我要试一下。 - ScottyBlades

0

Codable是Encodable和Decodable的类型别名。

因此,如果您要实现它,您需要实现以下两个函数。

  1. func encode(to: Encoder)
  2. init(from: Decoder)

5
这是因为OrderItem是一个协议。他/她不需要实现那些方法,因为他/她的类型已经包含了Codable类型。 - Pink
感谢您澄清这一点,Pink。 - Vlad

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