我目前正在浏览地标应用程序教程,我正在您将 JSON 加载到结构中的部分。它们给出以下代码。
参考: https://developer.apple.com/tutorials/swiftui/building-lists-and-navigation
import Foundation
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find (filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load (filename) from main bundle:n(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse (filename) as (T.self):n(error)")
}
}
我特别不理解T.self
引用,也不理解函数开头的<T: Decodable>
。有人能够分解该功能吗?谢谢。
我看到这个问题在评论中得到了回答,但我觉得对于那些刚接触 Swift 试图理解泛型的人来说,这是一个值得的实际答案。
为了回答这个问题,让我们看看T
是什么,它在函数的签名中定义
func load<T: Decodable>(_ filename: String) -> T
这表示泛型函数load(_:String) -> T
返回某种符合Decodable
的类型T
,因此T
是引用返回值类型的通用方式,其中该类型将从调用站点的上下文中确定,具体而言,由接收返回值的任何类型确定。
被问到的行是
return try decoder.decode(T.self, from: data)
这里T.self
是指类型T
本身的一种方式。 如果您查看decode
的函数签名,您会发现它看起来像这样:
func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T
请注意,它表示参数类型T.Type
,type
。 这意味着参数type
将保存类型T
而不是类型T
的值。 这是 Swift 区分指定变量是类型值与其值是类型本身的方法。 起初这可能有点令人困惑,但一旦您了解泛型,它就有意义了。
因为 Swift 是一种强类型语言,其中类型是在编译时定义的,所以decode
需要知道它试图解码的东西的类型。 在无类型语言中,如JavaScript或Python,这不是必需的。 在这些语言中,对象基本上是字典,因此它总是可以解码字典(或数组,或一小组基元类型之一)。在 Swift 中,struct
和class
不仅仅是字典的语法糖。 它们更像是 C 风格的struct
- 一堆二进制数据,具有由类型确定的特定内存布局。 为了正确解码,decode
必须知道它在解码什么,最常见的是,如何通过其init(from: Decoder) throws
方法(可能由编译器合成)告诉它自己解码。 它叫NSImage.init(from: Decoder)
,还是String.init(from: Decoder)
,等等...?
如果您熟悉在 OOP 中实现运行时多态性的方式,那么提供类型(即使是一般的)也提供了一种获取类型的协议见证表的方法 - 这有点像类用于虚拟方法的运行时动态调度的"vtable"的协议等效物。因此,它允许它做一些类似于在 OOP 中调用虚拟方法的事情,除了它通常可以在编译时弄清楚它,并且可以同时适用于值类型和引用类型。 了解他的工作方式还可以深入了解不同但相关的问题,例如为什么直接在协议中声明的所需方法与仅在协议扩展中声明的方法获得不同的多态行为(基本上直接在协议中声明的方法在见证表中,因此可以动态调度它们, 而仅在扩展中的那些不在见证表中,因此任何特定于类型的实现都将丢失,而是只能调用协议扩展中的实现)。
以下函数使用它来打印其参数的类型(有更好的方法可以做到这一点,但这说明了T.Type
和T.self
的角色):
func foo<T>(_ value: T)
{
let typeOfValue: T.Type = T.self
print("The value, (value), is of type, (typeOfValue).")
}
import Foundation
var landMarks: [Landmark] = load("landmarkData.json")
func load<T: Decodable>(_ filename: String) -> T {
...
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
...
}
您缺少此代码var landMarks: [Landmark] = load("landmarkData.json")
,因为func load(_:)->T
返回值类型是[地标]类型,因此T是[地标]数组类型。
这是我的理解