ios 协议只能用作通用约束,因为它有 Self 或 associatedType 要求
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36348061/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Protocol can only be used as a generic constraint because it has Self or associatedType requirements
提问by Rahul Katariya
I have a protocol RequestType and it has associatedType Model as below.
我有一个协议 RequestType 并且它具有如下所示的关联类型模型。
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]()
}
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.
但是我在网上收到错误let queue = [RequestType]()
,Protocol RequestType 只能用作通用约束,因为它具有 Self 或 associatedType 要求。
回答by Scott Thompson
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)
}
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:
Swift 允许你RequestType
以你想要的方式创建一个数组。我可以将这些请求类型的数组传递给函数:
func handleQueueOfRequests(queue: [RequestType]) {
// frobulate All The Things!
for request in queue {
request.frobulateModel(/* What do I put here? */)
}
}
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.
我想把所有事情都搞定,但我需要知道要传递到调用中的参数类型。我的一些RequestType
实体可以采用 a LegoModel
,一些可以采用 a PlasticModel
,而其他一些可以采用PeanutButterAndPeepsModel
。Swift 对这种歧义并不满意,因此它不会让您声明具有关联类型的协议的变量。
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.
同时,例如,RequestType
当我们知道所有这些都使用LegoModel
. 这似乎是合理的,而且确实如此,但是您需要某种方式来表达这一点。
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
}
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.
现在声明一个数组是完全合理的,LegoRequestType
因为如果我们想要frobulate
所有这些,我们知道我们LegoModel
每次都必须传入 a 。
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
.
关联类型的这种细微差别使得使用它们的任何协议都很特别。Swift 标准库中最显着的就是像这样的协议Collection
或Sequence
.
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.
为了允许您创建一组实现Collection
协议的事物或一组实现序列协议的事物,标准库采用了一种称为“类型擦除”的技术来创建结构类型AnyCollection<T>
或AnySequence<T>
. 类型擦除技术在 Stack Overflow 的回答中解释起来相当复杂,但如果你在网上搜索,有很多关于它的文章。
I can recommend a video from Alex Gallagher on Protocols With Associated Types (PATs)on YouTube.
我可以在 YouTube上推荐Alex Gallagher的一段关于关联类型协议 (PAT)的视频。
回答by Mojtaba Hosseini
From Swift 5.1
从 Swift 5.1
You can use an opaqueresult type to achieve something like that.
您可以使用不透明的结果类型来实现类似的目标。
imagine this:
想象一下:
protocol ProtocolA {
associatedtype number
}
class ClassA: ProtocolA {
typealias number = Double
}
So the following generates the error:
所以下面会产生错误:
var objectA: ProtocolA = ClassA() /* Protocol can only be used as a generic constraint because it has Self or associatedType requirements */
But making the type opaqueby adding the some
keyword before the type will fix the issue and usually thats the only thing we want:
但是通过在类型之前添加关键字来使类型不透明some
将解决问题,通常这是我们唯一想要的:
var objectA: some ProtocolA = ClassA()
回答by Farhan Arshad
A little change in design of your code could make it possible. Add an empty, non-associatedType, protocol at the top of your protocol hierarchy. Like this...
对代码设计稍加改动就可以使之成为可能。在协议层次结构的顶部添加一个空的、非关联类型的协议。像这样...
public protocol RequestTypeBase: class{}
public protocol RequestType: RequestTypeBase {
associatedtype Model
var path: Model? { get set } //Make it type of Model
}
public class RequestEventuallyQueue {
static let requestEventuallyQueue = RequestEventuallyQueue()
var queue = [RequestTypeBase]() //This has to be 'var' not 'let'
}
Another example, with classes derived from the protocol RequestType, making a queue and passing the queue to a function to print appropriate type
另一个例子,使用从协议 RequestType 派生的类,创建一个队列并将队列传递给一个函数以打印适当的类型
public class RequestA<AType>: RequestType{
public typealias Model = AType
public var path: AType?
}
public class RequestB<BType>: RequestType{
public typealias Model = BType
public var path: BType?
}
var queue = [RequestTypeBase]()
let aRequest: RequestA = RequestA<String>()
aRequest.path = "xyz://pathA"
queue.append(aRequest)
let bRequest: RequestB = RequestB<String>()
bRequest.path = "xyz://pathB"
queue.append(bRequest)
let bURLRequest: RequestB = RequestB<URL>()
bURLRequest.path = URL(string: "xyz://bURLPath")
queue.append(bURLRequest)
func showFailed(requests: [RequestTypeBase]){
for request in requests{
if let request = request as? RequestA<String>{
print(request.path!)
}else if let request = request as? RequestB<String>{
print(request.path!)
}else if let request = request as? RequestB<URL>{
print(request.path!)
}
}
}
showFailed(requests: queue)
回答by TimBigDev
Swift 5.1
斯威夫特 5.1
An examplehow you can use generic protocolsby implementing an associated typeand base protocol:
如何通过实现关联类型和基本协议来使用通用协议的示例:
import Foundation
protocol SelectOptionDataModelProtocolBase: class{}
protocol SelectOptionDataModelProtocol: SelectOptionDataModelProtocolBase {
associatedtype T
var options: Array<T> { get }
var selectedIndex: Int { get set }
}
class SelectOptionDataModel<A>: SelectOptionDataModelProtocol {
typealias T = A
var options: Array<T>
var selectedIndex: Int
init(selectedIndex _selectedIndex: Int, options _options: Array<T>) {
self.options = _options
self.selectedIndex = _selectedIndex
}
}
And an example View Controller:
和一个示例视图控制器:
import UIKit
struct Car {
var name: String?
var speed: Int?
}
class SelectOptionViewController: UIViewController {
// MARK: - IB Outlets
// MARK: - Properties
var dataModel1: SelectOptionDataModelProtocolBase?
var dataModel2: SelectOptionDataModelProtocolBase?
var dataModel3: SelectOptionDataModelProtocolBase?
// MARK: - Initialisation
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience init() {
self.init(title: "Settings ViewController")
}
init(title _title: String) {
super.init(nibName: nil, bundle: nil)
self.title = _title
self.dataModel1 = SelectOptionDataModel<String>(selectedIndex: 0, options: ["option 1", "option 2", "option 3"])
self.dataModel2 = SelectOptionDataModel<Int>(selectedIndex: 0, options: [1, 2, 3])
self.dataModel3 = SelectOptionDataModel<Car>(selectedIndex: 0, options: [Car(name: "BMW", speed: 90), Car(name: "Toyota", speed: 60), Car(name: "Subaru", speed: 120)])
}
// MARK: - IB Actions
// MARK: - View Life Cycle
}
回答by Lew Winczynski
This error may also occur in the following scenario:
在以下场景中也可能发生此错误:
protocol MyProtocol {
assosciatedtype SomeClass
func myFunc() -> SomeClass
}
struct MyStuct {
var myVar = MyProtocol
}
In this case, all you have to do to fix the issue is to use generics:
在这种情况下,解决问题所需要做的就是使用泛型:
protocol MyProtocol {
assosciatedtype SomeClass
func myFunc() -> SomeClass
}
struct MyStuct<T: MyProtocol> {
var myVar = T
}