如何在方法声明

时间:2016-12-10 20:30:11

标签: ios swift generics

我正在构建一个解析服务器响应的通用方法。可能我想做得太花哨,但另一方面我会在很多项目中使用它,所以也许值得花一些时间在它上面,让我们描述我遇到的问题。

我实现了响应骨架,它是一个通用类,我将在其中创建对象。对象是M

的类型
class ApiResponse<M: Mappable> {  
    required init(response: Any) {
        // Here I'd like to do mapping according to the holding type
    }
}

然后对于每个端点,我创建了一个类,用于定义服务器返回的对象类型

class PostsResponse: ApiResponse<Post> {

}

api客户端具有确定响应类型的每个端点的方法,因此从现在起就知道每种类型。这是一个例子:

var posts: Observable<PostsResponse> {
    get { return getObservable(endpoint: .posts) }
}

到目前为止一切顺利。当我为获取观察者创建“Swifty”方法时,问题从这里开始(见下文)。 ApiResponse是泛型类型,因此在getObservable的声明中,编译器想知道确切的类型,第二个问题是当我调用Response类的构造函数时,因为某种方式{{1}没有可访问的初始化程序。

T

问题是如何让漫画者高兴。

以下是代码https://github.com/artur-gurgul/babylon-partners

的链接

更新

经过几个小时的斗争,我已经让它发挥了作用,但是我发现了一些奇怪的东西。

我将方法签名写为fallowing

private func getObservable<T>(endpoint: Endpoint) -> Observable<T> where T:ApiResponse {
    return Observable.create({ observer -> Disposable in
        Alamofire
            .request(endpoint.url, method: .get)
            .responseJSON(completionHandler: { response in
                switch response.result {
                case .success(let json):
                    let parsedResponse = T(response: json)
                    observer.onNext(parsedResponse)
                    observer.onCompleted()
                case .failure(let error):
                    observer.onError(error)
                }
            })
        return Disposables.create()
    })
}

当我删除private func getObservable<T: ApiResponse<A>, A: Mappable>(endpoint: Endpoint, t:A?=nil) -> Observable<T> where A:NSManagedObject 时,编译器不满意。它抱怨方法签名中没有使用t:A?=nil类型。为什么会这样?为什么?为什么?为什么呢?

1 个答案:

答案 0 :(得分:0)

这似乎是Swift编译器中当前类型推断实现的缺陷/限制。虽然在您的示例中期望编译器能够推断出两个嵌套级别的泛型类型约束(AT)是合乎逻辑的,但我猜这种多级推断不是现在没有实现,这意味着Swift希望您在签名本身中使用所有泛型类型约束,因此编译器可以从任何调用代码的上下文推断出AT应该是什么而不需要不止一个层次的解决,例如不要求它理解:

result = PostResponse             <----->    T = PostResponse

PostResponse: ApiResponse<Post>   <----->    T: ApiResponse<Post>

Post is the inner generic type    <----->    therefore A: Post

相反,你必须给Swift一个直接的上下文方式来推断A,而不必首先推断和反省T

上面的解决方案确实在签名中提供了A,这使编译器保持沉默(尽管我认为这种方法不会真正起作用,请参阅我的第二个注释)。更传统的方法是要求调用您的函数的代码明确指定A的类型作为参数,例如:

private func getObservable<T: ApiResponse<A>, A: Mappable>(endpoint: Endpoint, objectType:A.Type) -> Observable<T> where A:NSManagedObject 

这将在您的代码中调用,如下所示:

var posts: Observable<PostsResponse> {
    get { return getObservable(endpoint: .posts, objectType:Post.self) }
}

几个旁注:

  1. 我认为您不需要指定A:Mappable约束,因为类声明class ApiResponse<M:Mappable>已经声明了该要求
  2. 即使你的变通方法示例中你有一个带有nil默认值(t:A? = nil的参数)会使编译器警告无效,如果你试图实际调用它时没有得到不同的警告,我会感到惊讶在代码中的某个地方运行。参数t:A?的nil默认值使编译器无法直接推断出具体类型A应该是什么,我之前说过,我不认为Swift编译器能够以其他方式推断{ {1}}首先在A上推断然后进行内省。