问题描述
我有一个协议 RequestType,它有如下关联类型模型.
I have a protocol RequestType and it has associatedType Model as below.
public protocol RequestType: class {
associatedtype Model
var path: String { get set }
}
public extension RequestType {
public func executeRequest(completionHandler: Result<Model, NSError> -> Void) {
request.response(rootKeyPath: rootKeyPath) { [weak self] (response: Response<Model, NSError>) -> Void in
completionHandler(response.result)
guard let weakSelf = self else { return }
if weakSelf.logging { debugPrint(response) }
}
}
}
现在我正在尝试为所有失败的请求创建一个队列.
Now I am trying to make a queue of all failed requests.
public class RequestEventuallyQueue {
static let requestEventuallyQueue = RequestEventuallyQueue()
let queue = [RequestType]()
}
但我在 let queue = [RequestType]()
行收到错误,即 Protocol RequestType 只能用作通用约束,因为它具有 Self 或 associatedType 要求.
But I get the error on line let queue = [RequestType]()
that Protocol RequestType can only be used as a generic constraint because it has Self or associatedType requirements.
推荐答案
假设我们现在调整您的协议以添加一个使用关联类型的例程:
Suppose for the moment we adjust your protocol to add a routine that uses the associated type:
public protocol RequestType: class {
associatedtype Model
var path: String { get set }
func frobulateModel(aModel: Model)
}
而 Swift 可以让你以你想要的方式创建一个 RequestType
数组.我可以将这些请求类型的数组传递给函数:
And Swift were to let you create an array of RequestType
the way you want to. I could pass an array of those request types into a function:
func handleQueueOfRequests(queue: [RequestType]) {
// frobulate All The Things!
for request in queue {
request.frobulateModel(/* What do I put here? */)
}
}
我想弄清楚所有事情,但我需要知道要传递给调用的参数类型.我的一些 RequestType
实体可以采用 LegoModel
,有些可以采用 PlasticModel
,而其他的可以采用 PeanutButterAndPeepsModel
.Swift 对歧义不满意,因此它不允许您声明具有关联类型的协议变量.
I get down to the point that I want to frobulate all the things, but I need to know what type of argument to pass into the call. Some of my RequestType
entities could take a LegoModel
, some could take a PlasticModel
, and others could take a PeanutButterAndPeepsModel
. Swift is not happy with the ambiguity so it will not let you declare a variable of a protocol that has an associated type.
同时,例如,当我们知道它们都使用 LegoModel
时,创建一个 RequestType
数组是非常有意义的.这似乎是合理的,确实如此,但您需要某种方式来表达这一点.
At the same time it makes perfect sense to, for example, create an array of RequestType
when we KNOW that all of them use the LegoModel
. This seems reasonable, and it is, but you need some way to express that.
一种方法是创建一个将真实类型与抽象模型类型名称相关联的类(或结构或枚举):
One way to do that is to create a class (or struct, or enum) that associates a real type with the abstract Model type name:
class LegoRequestType: RequestType {
typealias Model = LegoModel
// Implement protocol requirements here
}
现在声明一个 LegoRequestType
数组是完全合理的,因为如果我们想 frobulate
所有这些,我们知道我们必须传入一个 LegoModel
每次.
Now it's entirely reasonable to declare an array of LegoRequestType
because if we wanted to frobulate
all of them we know we would have to pass in a LegoModel
each time.
关联类型的这种细微差别使得任何使用它们的协议都变得特别.Swift 标准库有类似这样的协议,最著名的是 Collection
或 Sequence
.
This nuance with Associated Types makes any protocol that uses them special. The Swift Standard Library has Protocols like this most notably Collection
or Sequence
.
为了允许您创建实现 Collection
协议的事物数组或实现序列协议的事物集合,标准库采用了一种称为类型擦除"的技术来创建结构类型 AnyCollection<T>
或 AnySequence<T>
.类型擦除技术在 Stack Overflow 答案中解释起来相当复杂,但如果你在网上搜索,就会有很多关于它的文章.
To allow you to create an array of things that implement the Collection
protocol or a set of things that implement the sequence protocol, the Standard Library employs a technique called "type-erasure" to create the struct types AnyCollection<T>
or AnySequence<T>
. The type-erasure technique is rather complex to explain in a Stack Overflow answer, but if you search the web there are lots of articles about it.
我可以推荐一个来自 Alex Gallagher 关于关联类型 (PAT) 协议的视频在 YouTube 上.
I can recommend a video from Alex Gallagher on Protocols With Associated Types (PATs) on YouTube.
这篇关于协议只能用作通用约束,因为它具有 Self 或 associatedType 要求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!