为什么我们不能在函数中使用协议"可编码"作为类型?



我正在尝试通过符合协议Encodable编码模型获取数据。但它无法像下面的代码一样调用 funcencode

// MARK: - Demo2
class TestClass2: NSObject, Encodable {
var x = 1
var y = 2
}

var dataSource2: Encodable?
dataSource2 = TestClass2()
// error: `Cannot invoke 'encode' with an argument list of type '(Encodable)'`
let _ = try JSONEncoder().encode(dataSource2!)
//func encode<T>(_ value: T) throws -> Data where T : Encodable

但是在另一个演示中,它运行良好,为什么?

// MARK: - Demo1
protocol TestProtocol {
func test()
}
class TestClass1: NSObject, TestProtocol {
func test() {
print("1")
}
var x = 1
var y = 2
}

var dataSource1: TestProtocol?
dataSource1 = TestClass1()

func logItem(_ value: TestProtocol) {
value.test()
}
logItem(dataSource1!)

解决方案 1。

试试这段代码,它扩展了可编码的

extension Encodable {
func toJSONData() -> Data? { try? JSONEncoder().encode(self) }
}

解决方案 2.

避免使用扩展名污染 Apple 提供的协议

protocol MyEncodable: Encodable {
func toJSONData() -> Data?
}
extension MyEncodable {
func toJSONData() -> Data?{ try? JSONEncoder().encode(self) }
}

var dataSource2: Encodable?
dataSource2 = TestClass2()
let data = dataSource2?.toJSONData()

你不能传递协议,但你可以使用泛型来要求符合协议的类:

func printJSON<T: Encodable>(_ data: T) {
if let json = try? JSONEncoder().encode(data) {
if let str = String(data: json, encoding: .utf8) {
print(str)
}
}
}
// Now this should work
var dataSource2 = TestClass2()
printJSON(dataSource2!)

有许多方法可以解决这个问题。

@SPatel扩展Encodable的解决方案是一种可能性。 但是,我个人尽量避免用扩展来污染Apple提供的协议。

如果我在字里行间阅读,似乎您想要的是将任何符合Encodable的构造传递给其他结构/类中的函数/方法。

让我们举个例子,我认为您要实现的目标

struct Transform {
static func toJson(encodable: Encodable) throws -> Data {
return try JSONEncoder().encode(encodable)
}
}

但是,Xcode 会抱怨:

Protocol type 'Encodable' cannot conform to 'Encodable' because only concrete types can conform to protocols

一个 Swift-ier 解决方案是在函数上使用受约束的泛型:

struct Transform {
static func toJson<EncodableType: Encodable>(encodable: EncodableType) throws -> Data {
return try JSONEncoder().encode(encodable)
}
}

现在编译器可以推断出符合Encodable的类型,我们可以按预期调用函数:

let dataSource = TestClass2()
let jsonData = try? Transform.toJson(encodable: dataSource)

您的 2 个示例不同。

JSONEncoder((.encode(( 期望一个符合 procotolEncodable的具体类。引用dataSource2包含协议而不是具体类。

另一方面,logItem只接受协议作为输入,没有符合协议的具体类。这是您的示例之间的区别,以及为什么您的第二个案例有效而第一个案例无效。

使用您当前的设置,它将不起作用。您需要将一个具体的类传递给JSONEncoder。

只要TestClass2Encodable就可以使用以下代码。encode应该知道要编码什么。它引用类属性来执行此操作。Encodable本身不包含任何属性。

JSONEncoder().encode(dataSource2 as! TestClass2)

难道你只是在寻找这个吗?

func blah<T>(_ thing: T) where T: Codable {
do {
let data = try JSONEncoder().encode(thing)
.. do something with data
} catch {
print("severe problem in blah, couldn't encode")
}
}

相关内容

  • 没有找到相关文章

最新更新