Ti stai avvicinando nel modo sbagliato: in Swift, a differenza di Objective-C, le classi hanno tipi specifici e hanno persino una gerarchia di ereditarietà (ovvero, se la classe Beredita da A, quindi B.Typeeredita anche da 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
Ecco perché non dovresti usare AnyClass, a meno che tu non voglia davvero consentire qualsiasi classe. In questo caso sarebbe il tipo giusto T.Type, perché esprime il collegamento tra il returningClassparametro e il parametro della chiusura.
Infatti, usarlo invece di AnyClassconsentire al compilatore di inferire correttamente i tipi nella chiamata al metodo:
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())
}
Ora c'è il problema di costruire un'istanza di cui Tpassare handler: se provi ad eseguire subito il codice, il compilatore si lamenterà che Tnon è costruibile (). E giustamente: Tdeve essere esplicitamente vincolato per richiedere che implementi un inizializzatore specifico.
Questo può essere fatto con un protocollo come il seguente:
protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
Quindi devi solo cambiare i vincoli generici di invokeServiceda <T>a <T: Initable>.
Mancia
Se ricevi strani errori come "Impossibile convertire il tipo di espressione '()' in 'String'", è spesso utile spostare ogni argomento della chiamata del metodo nella propria variabile. Aiuta a restringere il codice che causa l'errore e a scoprire i problemi di inferenza del tipo:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
Ora ci sono due possibilità: l'errore si sposta su una delle variabili (il che significa che c'è la parte sbagliata) o viene visualizzato un messaggio criptico come "Impossibile convertire il tipo dell'espressione ()in tipo ($T6) -> ($T6) -> $T5".
La causa di quest'ultimo errore è che il compilatore non è in grado di inferire i tipi di ciò che hai scritto. In questo caso il problema è che Tviene utilizzato solo nel parametro della chiusura e la chiusura che hai superato non indica alcun tipo particolare, quindi il compilatore non sa quale tipo dedurre. Modificando il tipo di returningClassincludere, Tsi dà al compilatore un modo per determinare il parametro generico.
CastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }