我有一个调用 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’类型'"
CastDAO.invokeService("test", withParams: ["test" : "test"], returningClass: CityInfo.self) { cityInfo in /*...*/
}
编辑:
我尝试使用 object_getClass
,如 holex 所述,但现在我得到:
error: "Type 'CityInfo.Type' does not conform to protocol 'AnyObject'"
需要做什么才能符合协议(protocol)?
class CityInfo : NSObject {
var cityName: String?
var regionCode: String?
var regionName: String?
}
最佳答案
您以错误的方式处理它:在 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)来完成:
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
您为编译器提供了一种确定泛型参数的方法。
关于swift - 如何将类类型作为函数参数传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24308975/