前言
一般情况下,咱们修复问题都会将咱们能想到的过错case处理,但有时仍是会有意想不到的副作用产生。这篇文章介绍的便是Double Protocol Conformance
带来的问题
什么是 Double Protocol Conformance
简单说便是一个类型被声明屡次遵从相同协议
// Item 已经遵从了Codable协议
public struct Item: Codable { }
// Error: Redundant conformance of 'Item' to protocol 'Decodable'
// Error: Redundant conformance of 'Item' to protocol 'Encodable'
// Item 的扩展遵从了Codable协议
extension Item: Codable { }
假如咱们在同一个Target里边书写上面的代码会报错
有趣的部分
- 咱们在同一个Target里边,对一个类型和这个类型的扩展一起声明遵从相同协议,Xcode编译器会检测出来并报错
- 但当咱们在不同的Target里分别对一个类型和这个类型的扩展声明遵从相同协议时,Xcode就检测不出来了
在TargetA中声明Item遵从Codable,并运用编译器的默许完成
// TargetA
public struct Item: Codable {
public let id: UUID
public let name: String
public init(id: UUID, name: String) {
self.id = id
self.name = name
}
}
在TargetB中引进TargetA,并在Item的扩展中声明遵从Codable协议,自定义完成编解码
// TargetB
import Foundation
import TargetA
extension Item: Codable {
// MARK: - Coding Keys
enum CodingKeys: String, CodingKey {
case id = "product_id"
case name = "product_name"
}
// MARK: - Encodable
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(name, forKey: .name)
}
// MARK: - Decodable
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
let id = try values.decode(UUID.self, forKey: .id)
let name = try values.decode(String.self, forKey: .name)
self = .init(id: id, name: name)
}
}
此时Xcode不会报错,而只会弹出警告。但就隐藏着问题了。只需代码能Run,咱们就会认为没啥问题
露出问题
假设咱们有3个Target
- TargetA里边声明了Item,可是没有遵从Codable协议
- TargetB依靠TargetA,并在Item的扩展中遵从了Codable协议
- TargetC依靠TargetA,并未依靠TargetB,也在Item的扩展中遵从了Codable协议
看起来咱们的代码如同没啥问题,咱们也很难觉察到有Double Protocol Conformance
的问题
假如咱们在别的的Target里引进了TargetA、TargetB、TargetC,引进的顺序会影响终究的完成
// 先引进B再引进C
import TargetA
import TargetB
import TargetC
let item = Item(id: .init(), name: "My Item")
let encoder = JSONEncoder()
let encoded = try encoder.encode(item)
let jsonString = String(data: encoded, encoding: .utf8)
/** 输出的成果如下
{
"product_name_B": "My Item",
"product_id_B": "42A18A37-51FA-422C-9BB9-46D382CDF1CA"
}
*/
// 先引进C再引进B
import TargetA
import TargetC
import TargetB
let item = Item(id: .init(), name: "My Item")
let encoder = JSONEncoder()
let encoded = try encoder.encode(item)
let jsonString = String(data: encoded, encoding: .utf8)
/** 输出的成果如下
{
"product_name_C": "My Item",
"product_id_C": "42A18A37-51FA-422C-9BB9-46D382CDF1CA"
}
*/
- 从上面的比如中,咱们可以知道当一个类在不同的Target中一起遵从协议的时分,选用的是完成是第一个import中的完成
- 咱们一般在工作中都是模块化的,会比比如里边的情况更复杂。所以关于协议遵从指定一套规则是很重要的
资料
alexanderweiss.dev/blog/2023-0…