ios - 如何在方法声明中使用关联类型

标签 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 的构造函数时类,因为不知何故 T没有可访问的初始化程序。

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()
    })
}

问题是如何让编译器开心。

这是代码的链接 https://github.com/artur-gurgul/babylon-partners


更新

经过几个小时的努力解决这个问题后,我让它工作了,但是我发现了一些奇怪的东西。

我把方法签名写成休闲

private func getObservable<T: ApiResponse<A>, A: Mappable>(endpoint: Endpoint, t:A?=nil) -> Observable<T> where A:NSManagedObject 

当我删除 t:A?=nil 时编译器不高兴。它提示 A方法签名中未使用类型。为什么会这样?,为什么?为什么?为什么?

最佳答案

这似乎是当前 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 编译器能够以其他方式推断出 A。通过先推断再反省 T .

关于ios - 如何在方法声明中使用关联类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41079993/

相关文章:

ios - 使用 NSUTF8StringEncoding 将 NSData 转换为 NSString 对于特殊字符不起作用

iphone - Facebook SDK 3.2 for iOS 分享描述多于三行不出现

ios - 如何将数据添加到firebase数据库中的特定使用引用?

ios - 删除 subview 不起作用

c# - 带有 It.IsAny 的 Expression<Func<T, bool>> 总是返回 true

c# - 嵌套泛型 <T1<T2>>

ios - 如何在表格 View 单元格的顶部添加阴影?

ios - didMoveToParentViewController 调用了两次

ios - 在 swift 错误 : Use of unresolved identifier 'ABXXXView' 中创建我自己的 cocoapods 库

java - 子类型 "promise"如何调用更具体类型的回调?