我有一个协议RequestType,它有如下的associatedType模型。
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要求。
6条答案
按热度按时间x0fgdtte1#
假设我们现在调整协议,添加一个使用相关类型的例程:
Swift允许你创建一个
RequestType
的数组,我可以将这些请求类型的数组传递给一个函数:我想把所有的东西都弄清楚,但是我需要知道传入调用的参数类型,一些
RequestType
实体可以接受LegoModel
,一些可以接受PlasticModel
,而其他的可以采用PeanutButterAndPeepsModel
。Swift不喜欢这种模糊性,因此它不会让您声明具有关联类型的协议变量。同时,创建一个
RequestType
的数组也是很有意义的,例如,当我们知道所有的数组都使用LegoModel
时,这看起来很合理,确实如此,但你需要某种方式来表达它。一种方法是创建一个类(或结构,或枚举),将一个真实的类型与抽象Model类型名称相关联:
现在声明一个
LegoRequestType
数组是完全合理的,因为如果我们想对所有的frobulate
数组进行声明,我们知道每次都必须传入一个LegoModel
。Swift标准库中有类似的协议,最著名的是
Collection
或Sequence
。为了允许您创建一个实现
Collection
协议的数组或一组实现序列协议的对象,标准库采用了一种称为“类型擦除”的技术来创建结构体类型AnyCollection<T>
或AnySequence<T>
。类型擦除技术在“堆栈溢出”答案中解释起来相当复杂,但如果你搜索网络,有很多关于它的文章。Swift 5.7存在性
Swift 5.7使用
any
关键字引入了显式存在,这将消除“Protocol can be only used as a generic constraint...”错误,但它并没有解决 * 本例 * 的基本问题。(诚然,这个例子是学术性的,出于演示的目的,而且由于其局限性,在真实的代码中可能没有用。但它也说明了显式存在不是万能的。)下面是使用Swift 5.7和
any
关键字的代码示例。现在,我们的队列包含了一个存在体集合,我们不再有“type cannot be used here because of Self or AssociatedType constraints”的错误。但是,它并没有解决这个例子中的底层问题,因为
frobulateModel
方法仍然可以接受任意类型(符合RequestType
协议的实体的关联类型)。Swift提供了其他机制来帮助弥补这一点。一般来说,您需要约束
Model
的值以公开所有Models
共享的行为。frobulateModel
方法可能会成为泛型,并对参数进行约束以遵循该协议。或者,您可以使用Swift 5.7的主要关联类型(SE-0346),以帮助在协议级别约束Models
的行为。因此,是的,显式存在可以删除OP所要求的错误消息--但它们并不是每种情况的解决方案。
另外,请记住,存在主义会导致间接性,这可能会带来性能问题。在他们的WWDC会议上,苹果警告我们要明智地使用它们。
gcxthw6b2#
来自Swift 5.1 - Xcode 11
您可以使用opaque结果类型来实现类似的功能。
想象一下:
因此,下面的代码会生成错误:
但是通过在类型之前添加
some
关键字来使类型不透明可以解决这个问题,通常这是我们唯一想要的:2fjabf4q3#
雨燕5.1
一个示例如何通过实现关联类型和基本协议来使用 * 通用协议 *:
以及一个示例视图控制器:
y3bcpkx14#
在你的代码设计上做一点小小的改变就可以做到这一点。在你的协议层次结构的顶部添加一个空的、非关联类型的协议。就像这样...
另一个例子是,用从协议RequestType派生的类,创建一个队列,并将队列传递给一个函数以打印相应的类型
gj3fmq9x5#
在下列情况下也可能发生此错误:
在这种情况下,要解决此问题,只需使用泛型:
ycggw6v26#
Swift 5.7中存在的
any
🪛现在,我们可以通过在调用点使用
any
关键字来解决“此协议不能用作通用约束,因为它具有Self
或associatedType
要求”的问题:Xcode 14现在建议将此更改作为一种修复方法,错误就会消失!
警告:尽可能使用改进的泛型语法
目前,泛型比存在型
any
功能更全面,性能更好,所以我们可能更喜欢使用存在型any
,尽管它有局限性。为了更容易地使用正确的泛型语法,我们可以使用
some
关键字来为具有单个泛型参数的函数指定泛型(这被称为主关联类型)。