我有一个用于发出和处理 JSON API 请求的通用类。我传入 TParam
和 TResult
模板参数,但当我使用派生类型时,不会调用它的实现。
下面是一些您可以放入 Playground 中进行说明的代码:
import Cocoa
// Base class for parameters to POST to service
class APIParams {
func getData() -> Dictionary<String, AnyObject> {
return Dictionary<String, AnyObject>()
}
}
// Base class for parsing a JSON Response
class APIResult {
func parseData(data: AnyObject?) {
}
}
// Derived example for a login service
class DerivedAPIParams: APIParams {
var user = "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ef9c80828aaf80818ac18c8082" rel="noreferrer noopener nofollow">[email protected]</a>"
var pass = "secret"
// THIS METHOD IS CALLED CORRECTLY
override func getData() -> Dictionary<String, AnyObject> {
return [ "user": user, "pass": pass ]
}
}
// Derived example for parsing a login response
class DerivedAPIResult: APIResult {
var success = false
var token:String? = ""
// THIS METHOD IS NEVER CALLED
override func parseData(data: AnyObject?) {
/*
self.success = data!.valueForKey("success") as Bool
self.token = data!.valueForKey("token") as? String
*/
self.success = true
self.token = "1234"
}
}
class APIOperation<TParams: APIParams, TResult: APIResult> {
var url = "http://localhost:3000"
func request(params: TParams, done: (NSError?, TResult?) -> ()) {
let paramData = params.getData()
// ... snip making a request to website ...
let result = self.parseResult(nil)
done(nil, result)
}
func parseResult(data: AnyObject?) -> TResult {
var result = TResult.self()
// This should call the derived implementation if passed, right?
result.parseData(data)
return result
}
}
let derivedOp = APIOperation<DerivedAPIParams, DerivedAPIResult>()
let params = DerivedAPIParams()
derivedOp.request(params) {(error, result) in
if result? {
result!.success
}
}
真正奇怪的是,只有 DerivedAPIResult.parseData()
没有被调用,而 DerivedAPIParams.getData()
方法被调用。有什么想法吗?
最佳答案
更新:此缺陷已在 XCode 6.3 beta1(Apple Swift 版本 1.2 (swiftlang-602.0.37.3 clang-602.0.37))中修复
添加了使用 XCode 6.1 (Swift 1.1) 时的解决方法的信息 有关详细信息,请参阅这些开发论坛主题: https://devforums.apple.com/thread/251920?tstart=30 https://devforums.apple.com/message/1058033#1058033
在一个非常相似的代码示例中,我遇到了完全相同的问题。在等待一个又一个的测试版“修复”之后,我做了更多的挖掘,发现我可以通过使基类 init() 成为必需的来获得预期的结果。
举例来说,这是 Matt Gibson 通过向 ApiResult 添加适当的 init() 来“修复”的简化示例
// Base class for parsing a JSON Response
class APIResult {
// adding required init() to base class yields the expected behavior
required init() {}
}
// Derived example for parsing a login response
class DerivedAPIResult: APIResult {
}
class APIOperation<TResult: APIResult> {
init() {
// EDIT: workaround for xcode 6.1, tricking the compiler to do what we want here
let tResultClass : TResult.Type = TResult.self
var test = tResultClass()
// should be able to just do, but it is broken and acknowledged as such by Apple
// var test = TResult()
println(test.self) // now shows that we get DerivedAPIResult
}
}
// Templated creation creates APIResult
let derivedOp = APIOperation<DerivedAPIResult>()
我不知道为什么会这样。如果我有时间,我会更深入地研究,但我最好的猜测是,由于某种原因,需要 init 会导致生成不同的对象分配/构造代码,从而强制正确设置我们希望的 vtable。
关于templates - 未调用通用类模板的派生类方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24193026/