Вы подходите к этому неправильно: в 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>.
Наконечник
Если вы получаете странные ошибки, такие как «Невозможно преобразовать тип выражения« () »в тип« Строка », часто бывает полезно переместить каждый аргумент вызова метода в его собственную переменную. Это помогает сузить код, вызывающий ошибку, и выявить проблемы с выводом типа:
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 }