Estou brincando com a resposta desta questão: Como testar se uma variável genérica é do tipo AnyObject
Essa solução parece não funcionar mais. Tenho este código:
public struct ObjectHashable<T>: Hashable {
public let object: T
public init(object: T) {
let t1 = type(of: object)
let b1 = t1 is AnyClass
print(t1) // Plugin
print(b1) // false
let t2 = T.self
let b2 = t2 is AnyClass
print(t2) // Plugin
print(b2) // false
let t3 = Mirror(reflecting: object).subjectType
let b3 = t3 is AnyClass
print(t3) // P
print(b3) // true
self.object = object
}
public static func ==(lhs: Self, rhs: Self) -> Bool {
return true
}
public func hash(into hasher: inout Hasher) {
}
}
protocol Plugin: AnyObject {}
class P: Plugin {}
typealias PluginHashable = ObjectHashable<Plugin>
PluginHashable(object: P())
Esse é um comportamento surpreendente.
1.
let t1 = type(of: object)
type(of:)
deveria retornar o tipo dinâmico, que é P
, não Plugin
. Mas obtive "Plugin" impresso.
2.
let b1 = t1 is AnyClass
Não deveria ser verdade, já que t1 é um tipo de referência? (com base na resposta aqui: Como testar se uma variável genérica é do tipo AnyObject )
3.
let t2 = T.self
Isso imprime Plugin
, o que faz sentido, porque eu Plugin
coloquei<>
4.
let b2 = t2 is AnyClass
Isso é falso, mas acho que deveria ser verdade pelo mesmo motivo
5.
let t3 = Mirror(reflecting: object).subjectType // P
Isso imprime P
, o que faz sentido
6.
let b3 = t3 is AnyClass // true
Isso é verdade, o que faz sentido. Mas por que é diferente dos outros dois casos?
Esse comportamento inesperado é documentado na seção chamada Encontrando o Tipo Dinâmico em um Contexto Genérico :
No seu exemplo,
P
seria oPlugin
protocolo. Para expandir a explicação na documentação, atype(of:)
função possui algumas regras de verificação de tipo incomuns .Ou seja, se o compilador perceber que seu parâmetro é um tipo existencial, ele permitirá que o tipo de toda a
type(of:)
expressão seja um metatipo concreto, que de outra forma seria um tipo totalmente não relacionado ao metatipo do tipo existencial.No
ObjectHashable
entanto, o tipo deobject
éT
. O compilador não sabe que this será vinculado ao tipo existencialany Plugin
posteriormente, portanto, não aplica esta regra especial de verificação de tipo para tipos existenciais.type(of: object)
é basicamente equivalente aT.self
.O comportamento de
type(of:)
foi alterado para funcionar assim neste commit , o que aconteceu depois que a resposta vinculada foi publicada.Como a documentação sugere, converter
object
para um tipo existencial (o mais fácil éAny
) antes de colocá-lo emtype(of:)
funcionaria como esperado.Em relação a 2 e 4, não há muito o que explicar. Eles simplesmente não são classes. Só porque o
Plugin
é restrito por classe não significa que o tipo existencialany Plugin
seja uma classe. Tipos existenciais são muito diferentes de tipos concretos. A SE-0335 propôs que todos os tipos existenciais fossem prefixados comany
para destacar essa diferença.