为什么这不符合我的期望(希望)呢?

时间:2018-04-19 15:10:31

标签: swift generics swift-protocols

我在我的框架中设置了几个协议来处理资源。在其中一个协议中,我设置了一个扩展,为decode函数提供默认实现。显示代码和发生的事情更简单(请参阅fatalError的调用)。在实际实现中有更多代码,但这说明了问题:

这是“基础”协议:

public protocol Resourceful {
    associatedtype AssociatedResource
    typealias ResourceCompletionHandler = (AssociatedResource?, Error?) -> Void

    func fetch(_ completion: @escaping ResourceCompletionHandler)
}

这是Resourceful:

的通用,具体实现
    open class WebResourceApiCall<Resource>: Resourceful {
    public typealias AssociatedResource = Resource
    public typealias FetchedResponse = (data: Data?, urlResponse: URLResponse?)

    public init() {
    }

    public func fetch(_ completion: @escaping ResourceCompletionHandler) {
        try! decode(fetched: (data: nil, urlResponse: nil))
    }

    public func decode(fetched: FetchedResponse) throws -> Resource {
        fatalError("It ends up here, but I don't want it to!")
    }
}

extension WebResourceApiCall where Resource: Decodable {
    public func decode(fetched: FetchedResponse) throws -> Resource {
        fatalError("This is where I want it to go...")
    }
}

这就是我试图使用它的方式:

public struct Something: Decodable { }

var apiCall = WebResourceApiCall<Something>()
apiCall.fetch { _, _ in } // Implictly calls decode... but not the decode I expected it to! See fatalError() calls...

不像我希望的那样在扩展名上调用decode,而是始终调用没有约束的“默认”decode方法。

为什么这不按照我期望的方式工作?

提前致谢!

1 个答案:

答案 0 :(得分:0)

Swift是一种静态调度的语言,因此要调用的decode()函数的地址是在编译时计算的,并且由于该调用发生在该类的基本定义内,因此编译器会选择原始实现。

现在,如果您从编译器具有足够信息来选择所需实现的地方调用该方法,那么它将起作用:

var apiCall = WebResourceApiCall<Something>()
try apiCall.decode(fetched: (nil, nil))

上面的代码将从专用扩展名中调用该方法,因为此时编译器可以更好地知道其具有更专用的实现。

如果在动态调度世界中(即在协议级别)移动decode()方法,应该可以实现所需的行为。