为什么我们不能将协议“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。
只要TestClass2
是Encodable
就可以使用下面的代码。 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")
}
}
我正在尝试通过符合 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。
只要TestClass2
是Encodable
就可以使用下面的代码。 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")
}
}