Ви підходите до нього неправильно: у Swift, на відміну від Objective-C, класи мають певні типи і навіть мають ієрархію успадкування (тобто якщо клас Bуспадковує від A, то B.Typeтакож успадковує від 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
Ось чому ви не повинні використовувати AnyClass, якщо ви дійсно не хочете дозволити будь-який клас. У цьому випадку правильний тип був би T.Type, оскільки він виражає зв'язок між returningClassпараметром і параметром закриття.
Фактично, використання його замість AnyClassдозволяє компілятору правильно зробити висновки про типи у виклику методу:
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())
}
Тепер існує проблема побудови екземпляра Tдля передачі handler: якщо ви спробуєте запустити код прямо зараз, компілятор поскаржиться на те, що Tне можна сконструювати (). І це правильно: Tмає бути явно обмежено, щоб вимагати, щоб він реалізував певний ініціалізатор.
Це можна зробити за допомогою такого протоколу:
protocol Initable {
init()
}
class CityInfo : NSObject, Initable {
var cityName: String?
var regionCode: String?
var regionName: String?
// Nothing to change here, CityInfo already implements init()
}
Тоді вам залишається лише змінити загальні обмеження invokeServiceз <T>на на <T: Initable>.
Порада
Якщо у вас з’являються дивні помилки, такі як «Неможливо перетворити тип виразу '()' у тип« String »», часто корисно перемістити кожен аргумент виклику методу до власної змінної. Це допомагає звузити код, що спричиняє помилку та виявляє проблеми виводу типу:
let service = "test"
let params = ["test" : "test"]
let returningClass = CityInfo.self
CastDAO.invokeService(service, withParams: params, returningClass: returningClass) { cityInfo in /*...*/
}
Тепер є дві можливості: помилка переходить до однієї зі змінних (це означає, що там неправильна частина) або ви отримуєте криптовалютне повідомлення типу "Неможливо перетворити тип виразу у ()тип ($T6) -> ($T6) -> $T5".
Причина останньої помилки полягає в тому, що компілятор не в змозі зробити висновки про типи написаного вами. У цьому випадку проблема полягає в тому, що Tвикористовується лише в параметрі закриття, а закрите вами рішення не вказує конкретного типу, тому компілятор не знає, який тип зробити. Змінюючи тип returningClassвключення, Tви даєте компілятору спосіб визначення загального параметра.
CastDAO.invokeService("test", withParams: ["test" : "test"]) { (ci:CityInfo) in }