我想为数据获取服务创建通用协议,如下所示:
protocol FetchDataDelegate: AnyObject {
associatedtype ResultData
func didStartFetchingData()
func didFinishFetchingData(with data: ResultData)
func didFinishFetchingData(with error: Error)
}
class Controller: UIViewController, FetchDataDelegate {
func didStartFetchingData() {
//...
}
func didFinishFetchingData(with data: ResultData) {
//...
}
func didFinishFetchingData(with error: Error) {
//...
}
}
class NetworkManager {
weak var delegate: FetchDataDelegate?
//...
}
Controller类将具有对NetworkManager实例的引用,通过这个,我想开始网络操作。操作结束后,我想调用适当的委托函数以将结果传递回控制器。但是使用此设置,我得到了以下错误: 协议“ FetchDataDelegate”只能用作通用约束,因为它具有“自我”或相关的类型要求 问题是我应该怎么做才能将此通用协议用作变量类型?或者,如果不可能的话,正确的方法是什么?谢谢!
答案 0 :(得分:2)
虽然这与question David Smith linked密切相关(并且您也应该阅读),但是值得单独回答,因为这是一个不同的具体用例,因此我们可以讨论它。
首先,假设您可以存储此变量。你会怎么做? NetworkManager
中的哪个函数可以调用delegate.didFinishFetchingData
?当您不知道ResultData
是什么时,您将如何生成它?
重点在于,这不是PAT(具有关联类型的协议)的目的。这不是他们的目标。他们的目标是帮助您向其他类型添加扩展,或限制可以将哪些类型的类型传递给通用算法。出于这些目的,它们非常强大。但是您想要的是泛型,而不是协议。
您应该使用泛型函数来处理特定调用的结果,而不是创建委托,而不是尝试将每个视图控制器固定为特定的结果类型(无论如何都不是很灵活)。例如,以最简单,最不灵活的方式仍然可以为您提供进度报告:
struct APIClient {
func fetch<Model: Decodable>(_: Model.Type,
with urlRequest: URLRequest,
completion: @escaping (Result<Model, Error>) -> Void)
-> Progress {
let session = URLSession.shared
let task = session.dataTask(with: urlRequest) { (data, _, error) in
if let error = error {
completion(.failure(error))
}
else if let data = data {
let decoder = JSONDecoder()
completion(Result {
try decoder.decode(Model.self, from: data)
})
}
}
task.resume()
return task.progress
}
}
let progress = APIClient().fetch(User.self, with: urlRequest) { user in ... }
此结构是解决整个问题类别的基本方法。 It can be made much, much more flexible depending on your specific needs.
(如果您的didStartFetchingData
方法非常重要,以Progress
无法解决的方式,请留下评论,我将向您展示如何实现这种方法。这并不困难,但这个答案已经很长了。)
答案 1 :(得分:0)
可能的this副本
总而言之,您不能将通用协议用作变量类型,因为您在编译时不知道<span style="display:none" id='ProjectTrackingCookie">
ProjectTrancingGuid
</span>
的类型,因此必须将其用作通用约束