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 B
eredita da A
, quindi B.Type
eredita 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 returningClass
parametro e il parametro della chiusura.
Infatti, usarlo invece di AnyClass
consentire 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 T
passare handler
: se provi ad eseguire subito il codice, il compilatore si lamenterà che T
non è costruibile ()
. E giustamente: T
deve 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 invokeService
da <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 T
viene 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 returningClass
includere, T
si dà al compilatore un modo per determinare il parametro generico.
CastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }