Ответ 1
Вы приближаетесь к нему не так: в 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
, вы даете компилятору способ определить общий параметр.