Você está abordando isso da maneira errada: no Swift, ao contrário do Objective-C, as classes têm tipos específicos e até uma hierarquia de herança (ou seja, se a classe é B
herdada de A
, então B.Type
também é herdada de A.Type
):
class A {}
class B: A {}
class C {}
// B inherits from A
let object: A = B()
// B.Type also inherits from A.Type
let type: A.Type = B.self
// Error: 'C' is not a subtype of 'A'
let type2: A.Type = C.self
É por isso que você não deve usar AnyClass
, a menos que queira realmente permitir qualquer aula. Nesse caso, o tipo correto seria T.Type
, porque expressa o link entre o returningClass
parâmetro e o parâmetro do fechamento.
De fato, usá-lo em vez de AnyClass
permitir que o compilador deduza corretamente os tipos na chamada do método:
class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: T.Type, completionHandler handler: ((T) -> ())) {
// The compiler correctly infers that T is the class of the instances of returningClass
handler(returningClass())
}
Agora existe o problema de construir uma instância T
para a qual passar handler
: se você tentar executar o código agora, o compilador reclamará que T
não é construtível ()
. E com razão: T
deve ser explicitamente restringido para exigir que implemente um inicializador específico.
Isso pode ser feito com um protocolo como o seguinte:
protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
Então você só precisa alterar as restrições genéricas de invokeService
de <T>
para <T: Initable>
.
Dica
Se você receber erros estranhos como "Não é possível converter o tipo da expressão '()' para o tipo 'String'", geralmente é útil mover todos os argumentos da chamada de método para sua própria variável. Ajuda a restringir o código que está causando o erro e a descobrir problemas de inferência de tipo:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
Agora, existem duas possibilidades: o erro é movido para uma das variáveis (o que significa que a parte errada está lá) ou você recebe uma mensagem enigmática como "Não é possível converter o tipo da expressão ()
em tipo ($T6) -> ($T6) -> $T5
".
A causa do último erro é que o compilador não pode inferir os tipos do que você escreveu. Nesse caso, o problema é que isso T
é usado apenas no parâmetro do fechamento e o fechamento que você passou não indica nenhum tipo específico, portanto o compilador não sabe que tipo inferir. Alterando o tipo de returningClass
inclusão, T
você fornece ao compilador uma maneira de determinar o parâmetro genérico.
CastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }