Ви підходите до нього неправильно: у 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 }