templates - 未调用通用类模板的派生类方法

标签 templates generics swift ios8 swift-playground

我有一个用于发出和处理 JSON API 请求的通用类。我传入 TParamTResult 模板参数,但当我使用派生类型时,不会调用它的实现。

下面是一些您可以放入 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/

相关文章:

c++ - 在函数声明中推导 shared_ptr T

java - 具有接口(interface)返回类型的通用方法

ios - 检测 UIScreenEdgeGestureRecognizer 并使 UIPanGestureRecognizer 失败

macos - 双击调整点大小时基于单元格的 NSTableView 调用函数?

c++ - 参数包展开顺序

c++ - 数组的部分模板成员特化

c++ - 为类模板忽略了用户定义的转换运算符(对于非模板则不然)

Java Generics - 插入内部类型参数

java - JPA CriteriaBuilder 泛型错误 : CriteriaQuery<capture#2-of ? > 不适用于参数 (Expression<Long>)

objective-c - 运行时无法识别 Objective-C 框架中的 Swift 类别