我想了解这一部分。
我正在阅读在通用上下文中查找动态类型,其中包含以下代码片段:
func printGenericInfo<T>(_ value: T) {
let t = type(of: value)
print("'\(value)' of type '\(t)'")
}
protocol P {}
extension String: P {}
let stringAsP: P = "Hello!"
printGenericInfo(stringAsP)
// 'Hello!' of type 'P'
...接下来是这句话:
出现这种意外结果是因为
内部对type(of: value)
的调用必须返回一个元类型,该元类型是printGenericInfo(_:)
的实例, 但T.Type
(预期的动态类型)不是String.self
的实例(值的具体元类型)。P.Type
String.self
不是 P.Type
的实例?func f(_ t: P.Type) { print("...") }
f(String.self)
type(of:)
在泛型函数外部而不是内部返回具体元类型?print("'\(stringAsP)' of type '\(type(of: stringAsP))'")
// 'Hello!' of type 'String'
对于协议
P
,您可以使用P
编写两种元类型。您可以写 (any P).Type
(又名 P.Protocol
),这是 P
本身的元类型,以及 any P.Type
(又名 P.Type
),这是一种存在类型,表示“符合 的某种类型的元类型” P
”。
String.self
是 any P.Type
的实例,但不是 (any P).Type
。这里的文档不正确。大概是想说(any P).Type
。否则这整件事就没有意义了。
type(of:)
有两种不同的行为,具体取决于其类型参数 T
的类型。你的 printGenericInfo
还有一个类型参数 T
,所以为了避免混淆,我将它们分别称为 typeof.T
和 printGenericInfo.T
。
如果
typeof.T
是不存在的类型,则返回 typeof.T
的元类型。如果 typeof.T
是存在(即协议)类型 any E
,它将返回 any E.Type
,而不是(any E).Type
。毕竟,前者更有用 - 它可以告诉您存在类型“包装”的实际类型。
type(of:)
在这种情况下需要“解开”存在主义并“看看里面”。
let s1 = ""
let s2: Any = s1
// typeof.T is String
type(of: s1)
// typeof.T is Any, an existential type, so type(of:) unwraps it and returns String.self
// this would be pretty useless if it just returned Any.self
type(of: s2)
这种行为上的差异正是你printGenericInfo
type(of:)
具有奇怪的签名 - 它返回一个
Metatype
类型参数,似乎与它所接受的类型无关。
type(of:)
使用特殊注释来允许编译器以特殊方式对
type(of:)
调用进行类型检查。根据
typeof.T
,
Metatype
可以是
typeof.T.Type
(当
typeof.T
不存在时)或
any typeof.T.Type
(当
typeof.T
存在时)。请注意,这是一个“编译时”检查。泛型函数的类型参数是在编译时决定的,而不是在运行时决定的。
printGenericInfo
不会打开 printGenericInfo.T
并查看其内部。它只是直接将其传递给
type(of:)
,因此
typeof.T
被决定为
printGenericInfo.T
,一个类型参数,它不是一个存在类型。
当你调用
printGenericInfo(stringAsP)
时,printGenericInfo.T
被决定为
any P
- 存在类型。这并没有改变
typeof.T
,它仍然是
printGenericInfo.T
,一种不存在的类型。所以在运行时,
type(of:)
返回
any P
的元类型,即
(any P).Type
。
如果您改为执行
let t = type(of: value as Any)
,那么 type(of:)
将会看到
typeof.T
是存在类型 (
Any
),因此它将解开存在类型并查看其内部。原因是您使用
let = "this is a string"
。
您不会(也不能)实例化
P
。 P
是一个协议,其他类型只能符合
它。但最终,当为变量创建值时,您必须显式实例化类型。该类型可能符合协议,也可能不符合。一致性不仅仅是身份。编辑,对您的问题进行更多澄清:
String.self
P
的实例。 这是因为在编写
extension String: P {}
时,你告诉它
String
的任何实例 都可以被视为
P
。这意味着类型本身
String.self
不符合
P
。 (回到正题:一致性不是同一性。)在这一行
let stringAsP: P = "Hello!"
String
。然后,编译器会生成此方法
printGenericInfo
,并将
P
作为类型。然后你可以想到
type(of:)
然后说“好吧,在方法定义中你说你传入了
P
,所以我会向你重复一遍。不需要费力地手动查找内容.”。如果您按照 Apple 的示例,首先将其强制转换为
Any
,则
type(of:)
实际上会执行一些工作并查找显式类型。