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