为什么我们不能将协议“Encodable”用作函数中的类型?

Why can't we use protocol `Encodable` as a type in the func?

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

// 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!)

你的两个例子不一样。

JSONEncoder().encode() 需要一个符合协议 Encodable 的具体 class。引用 dataSource2 包含一个协议而不是一个具体的 class.

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

您当前的设置无法正常工作。您需要将具体的 class 传递给 JSONEncoder。

只要TestClass2Encodable就可以使用下面的代码。 encode 应该知道要编码什么。它指的是 class 属性来做到这一点。 Encodable本身不包含任何属性。

JSONEncoder().encode(dataSource2 as! TestClass2)

解决方法一

试试这个代码,它扩展了 encodable

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

方案二

避免使用扩展污染 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()

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

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

如果我在字里行间阅读,看来您想要的是传递 任何符合 [=14 的构造 =] 到 其他 struct/class.

中的 function/method

让我们举个例子来说明我认为你正在努力实现的目标:

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)

您不能传递协议,但您可以使用泛型来要求 class 符合一个:

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!)

难道你只是在找这个?

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")
    }
}