Podchodzisz do tego w niewłaściwy sposób: w Swift, w przeciwieństwie do Objective-C, klasy mają określone typy, a nawet mają hierarchię dziedziczenia (to znaczy, jeśli klasa Bdziedziczy po A, to B.Typerównież dziedziczy po 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
Dlatego nie powinieneś używać AnyClass, chyba że naprawdę chcesz zezwolić na jakiekolwiek zajęcia. W tym przypadku właściwy byłby typ T.Type, ponieważ wyraża powiązanie między returningClassparametrem a parametrem zamknięcia.
W rzeczywistości użycie go zamiast AnyClasspozwala kompilatorowi poprawnie wywnioskować typy w wywołaniu metody:
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())
}
Teraz pojawia się problem konstruowania instancji Tdo przekazania handler: jeśli spróbujesz teraz uruchomić kod, kompilator narzeknie, że Tnie można go skonstruować (). I słusznie: Tmusi być wyraźnie ograniczone, aby wymagać implementacji określonego inicjatora.
Można to zrobić za pomocą protokołu takiego jak następujący:
protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
Wtedy wystarczy zmienić ogólne ograniczenia z invokeServiceod <T>do <T: Initable>.
Wskazówka
Jeśli otrzymujesz dziwne błędy, takie jak „Nie można przekonwertować typu wyrażenia '()' na typ 'String'”, często przydatne jest przeniesienie każdego argumentu wywołania metody do jego własnej zmiennej. Pomaga zawęzić kod, który powoduje błąd i odkryć problemy z wnioskiem o typie:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
Są teraz dwie możliwości: błąd przenosi się do jednej ze zmiennych (co oznacza, że jest tam zła część) lub pojawia się tajemniczy komunikat, taki jak „Nie można przekonwertować typu wyrażenia na ()typ ($T6) -> ($T6) -> $T5”.
Przyczyną tego drugiego błędu jest to, że kompilator nie jest w stanie wywnioskować typów tego, co napisałeś. W tym przypadku problem polega na tym, że Tjest on używany tylko w parametrze zamknięcia, a przekazane zamknięcie nie wskazuje żadnego konkretnego typu, więc kompilator nie wie, jaki typ wywnioskować. Zmieniając typ returningClassdo uwzględnienia T, dajesz kompilatorowi sposób na określenie parametru generycznego.
CastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }