Sie nähern sich dem falsch: In Swift haben Klassen im Gegensatz zu Objective-C bestimmte Typen und sogar eine Vererbungshierarchie ( B
dh wenn Klasse von erbt A
, dann B.Type
erbt auch von 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
Aus diesem Grund sollten Sie nicht verwenden AnyClass
, es sei denn , Sie wirklich zulassen wollen jede Klasse. In diesem Fall wäre der richtige Typ T.Type
, da er die Verknüpfung zwischen dem returningClass
Parameter und dem Parameter des Abschlusses ausdrückt .
Die Verwendung anstelle von AnyClass
ermöglicht es dem Compiler, die Typen im Methodenaufruf korrekt abzuleiten:
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())
}
Jetzt gibt es das Problem, eine Instanz T
zu handler
erstellen, an die übergeben werden soll : Wenn Sie versuchen, den Code jetzt auszuführen, beschwert sich der Compiler, dass dies T
nicht möglich ist ()
. Und das zu Recht: Muss T
explizit eingeschränkt werden, damit ein bestimmter Initialisierer implementiert werden kann.
Dies kann mit einem Protokoll wie dem folgenden erfolgen:
protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
Dann müssen Sie nur noch die generischen Einschränkungen von ändern invokeService
von <T>
bis<T: Initable>
.
Trinkgeld
Wenn seltsame Fehler wie "Der Typ '()' des Ausdrucks kann nicht in 'String' konvertiert werden" angezeigt werden, ist es häufig hilfreich, jedes Argument des Methodenaufrufs in eine eigene Variable zu verschieben. Es hilft dabei, den Code einzugrenzen, der den Fehler verursacht, und Probleme mit der Typinferenz aufzudecken:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
Jetzt gibt es zwei Möglichkeiten: Der Fehler wird in eine der Variablen verschoben (was bedeutet, dass der falsche Teil vorhanden ist) oder Sie erhalten eine kryptische Nachricht wie "Der Ausdruckstyp kann nicht konvertiert werden." ()
in Typ($T6) -> ($T6) -> $T5
".
Die Ursache für den letzteren Fehler ist, dass der Compiler nicht in der Lage ist, auf die von Ihnen geschriebenen Typen zu schließen. In diesem Fall besteht das Problem darin, dass T
nur der Parameter des Abschlusses verwendet wird und der von Ihnen übergebene Abschluss keinen bestimmten Typ angibt, sodass der Compiler nicht weiß, auf welchen Typ er schließen soll. Durch Ändern des Typs von returningClass
include T
geben Sie dem Compiler eine Möglichkeit, den generischen Parameter zu bestimmen.
CastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }