我有一个协议RequestType,它具有如下的relatedType模型。
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) } } } }
现在,我试图对所有失败的请求进行排队。
public class RequestEventuallyQueue { static let requestEventuallyQueue = RequestEventuallyQueue() let queue = [RequestType]() }
但是我在线上看到错误,let queue = [RequestType]()因为Protocol RequestType具有Self或associatedType要求,因此只能用作通用约束。
let queue = [RequestType]()
现在假设我们调整您的协议以添加一个使用关联类型的例程:
public protocol RequestType: class { associatedtype Model var path: String { get set } func frobulateModel(aModel: Model) }
Swift可以让您RequestType按照自己的方式创建数组。我可以将这些请求类型的数组传递给函数:
RequestType
func handleQueueOfRequests(queue: [RequestType]) { // frobulate All The Things! for request in queue { request.frobulateModel(/* What do I put here? */) } }
我到了要散布所有东西的地步,但是我需要知道将哪种类型的参数传递给调用。我的某些RequestType实体可以采用LegoModel,有些可以采用PlasticModel,而另一些可以采用PeanutButterAndPeepsModel。Swift对歧义性不满意,因此它不会让您声明具有关联类型的协议变量。
LegoModel
PlasticModel
PeanutButterAndPeepsModel
同时,例如,RequestType当我们知道它们所有人都使用时,创建一个数组非常有意义LegoModel。这似乎是合理的,而且确实如此,但是您需要某种表达方式。
一种方法是创建一个将真实类型与抽象Model类型名称相关联的类(或struct或enum):
class LegoRequestType: RequestType { typealias Model = LegoModel // Implement protocol requirements here }
现在声明一个数组是完全合理的,LegoRequestType因为如果我们想要frobulate所有这些数组,我们知道我们必须LegoModel每次都传递。
LegoRequestType
frobulate
与关联类型的这种细微差别使使用它们的协议变得特别。Swift标准库最著名的协议就是这样的Collection或Sequence。
Collection
Sequence
为了使您能够创建一组实现Collection协议的事物或一组实现序列协议的事物,标准库采用了一种称为“类型擦除”的技术来创建结构类型AnyCollection<T>或AnySequence<T>。要在Stack Overflow答案中进行解释,类型擦除技术相当复杂,但是如果您在网上搜索,则会有很多关于它的文章。
AnyCollection<T>
AnySequence<T>
我可以推荐来自Alex Gallagher的有关YouTube 上具有关联类型协议(PAT)的视频。