我有一个通用函数,该函数调用Web服务并将JSON响应序列化回一个对象。
class func invokeService<T>(service: String, withParams params: Dictionary<String, String>, returningClass: AnyClass, completionHandler handler: ((T) -> ())) { /* Construct the URL, call the service and parse the response */ }
我要完成的是等效于此Java代码
public <T> T invokeService(final String serviceURLSuffix, final Map<String, String> params, final Class<T> classTypeToReturn) { }
AnyClass
调用该方法时,我将其MyObject.self作为returningClass值传递,但是出现编译 错误“无法将表达式的类型’()’转换为’String’类型”
MyObject.self
CastDAO.invokeService(“test”, withParams: [“test” : “test”], returningClass: CityInfo.self) { cityInfo in /…/
}
编辑:
object_getClass如Holex所述,我尝试使用,但现在得到了:
object_getClass
错误:“类型’CityInfo.Type’不符合协议’AnyObject’”
需要做什么才能符合协议?
class CityInfo : NSObject { var cityName: String? var regionCode: String? var regionName: String? }
您用错误的方式进行处理:在Swift中,与Objective- C不同,类具有特定的类型,甚至具有继承层次结构(也就是说,如果类B继承自A,那么B.Type也继承自A.Type):
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参数和闭包的参数之间的链接。
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必须明确地约束它,要求它实现特定的初始化程序。
T
handler
()
这可以通过以下协议来完成:
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>。
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” 之类的神秘信息。
($T6) -> ($T6) -> $T5
后一个错误的原因是编译器无法推断您编写的内容的类型。在这种情况下,问题在于T仅在闭包的参数中使用,并且您传递的闭包没有指示任何特定类型,因此编译器不知道要推断的类型。通过更改包含的类型,returningClass可以T为编译器提供一种确定通用参数的方法。