Mam ogólną funkcję, która wywołuje usługę internetową i serializuje odpowiedź JSON z powrotem do obiektu.
class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: AnyClass, completionHandler handler: ((T) -> ())) {
/* Construct the URL, call the service and parse the response */
}
To, co próbuję osiągnąć, jest odpowiednikiem tego kodu Java
public <T> T invokeService(final String serviceURLSuffix, final Map<String, String> params,
final Class<T> classTypeToReturn) {
}
- Czy podpis mojej metody dla tego, co próbuję osiągnąć, jest prawidłowy?
- Dokładniej, czy określanie
AnyClass
jako typu parametru jest właściwe? - Wywołując metodę, przekazuję
MyObject.self
jako wartość returningClass, ale pojawia się błąd kompilacji „Nie można przekonwertować typu wyrażenia '()' na typ 'String'”
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/
}
Edytować:
Próbowałem użyć object_getClass
, jak wspomniał Holex, ale teraz otrzymuję:
błąd: „Typ 'CityInfo.Type' nie jest zgodny z protokołem 'AnyObject'”
Co należy zrobić, aby dostosować się do protokołu?
class CityInfo : NSObject {
var cityName: String?
var regionCode: String?
var regionName: String?
}
swift
generics
type-inference
Jean-Francois Gagnon
źródło
źródło
CastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }
Odpowiedzi:
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
B
dziedziczy poA
, toB.Type
również dziedziczy poA.Type
):Dlatego nie powinieneś używać
AnyClass
, chyba że naprawdę chcesz zezwolić na jakiekolwiek zajęcia. W tym przypadku właściwy byłby typT.Type
, ponieważ wyraża powiązanie międzyreturningClass
parametrem a parametrem zamknięcia.W rzeczywistości użycie go zamiast
AnyClass
pozwala kompilatorowi poprawnie wywnioskować typy w wywołaniu metody:Teraz pojawia się problem konstruowania instancji
T
do przekazaniahandler
: jeśli spróbujesz teraz uruchomić kod, kompilator narzeknie, żeT
nie można go skonstruować()
. I słusznie:T
musi być wyraźnie ograniczone, aby wymagać implementacji określonego inicjatora.Można to zrobić za pomocą protokołu takiego jak następujący:
Wtedy wystarczy zmienić ogólne ograniczenia z
invokeService
od<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:
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
T
jest 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 typreturningClass
do uwzględnieniaT
, dajesz kompilatorowi sposób na określenie parametru generycznego.źródło
T
służy do wyrażania relacji międzyreturningClass
obiektem a przekazanym docompletionHandler
. JeśliInitiable.Type
jest używany, ten związek jest tracony.func somefunc<U>()
możesz uzyskać klasę w
AnyObject
ten sposób:Swift 3.x
Swift 2.x
i jeśli chcesz, możesz później przekazać go jako parametr.
źródło
self.dynamicType
Mam podobny przypadek użycia w swift5:
źródło
Static method … requires that 'MyDecodable.Type' conform to 'Decodable'
. Czy mógłbyś zaktualizować swoją odpowiedź, aby podać przykładowe wezwanieloadItem
?.Type
i.self
(typem a metatypem).Zastosowanie
obj-getclass
:Zakładając, że ja jest obiektem informacji o mieście.
źródło
Szybki 5
Nie do końca ta sama sytuacja, ale miałem podobny problem. W końcu pomogło mi to:
Następnie możesz wywołać to wewnątrz instancji wspomnianej klasy w następujący sposób:
Mam nadzieję, że to pomoże komuś w tej samej sytuacji.
źródło