ios - usan - El protocolo solo se puede usar como una restricción genérica porque tiene requisitos Self o asociado
seo h3 (2)
Tengo un protocolo RequestType y tiene asociado el Modelo de tipo como se muestra a continuación.
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) }
}
}
}
Ahora estoy tratando de hacer una cola de todas las solicitudes fallidas.
public class RequestEventuallyQueue {
static let requestEventuallyQueue = RequestEventuallyQueue()
let queue = [RequestType]()
}
Pero recibo el error en línea
let queue = [RequestType]()
que Protocol RequestType solo se puede usar como una restricción genérica porque tiene requisitos Self o asociado.
Supongamos por el momento que ajustamos su protocolo para agregar una rutina que use el tipo asociado:
public protocol RequestType: class {
associatedtype Model
var path: String { get set }
func frobulateModel(aModel: Model)
}
Y Swift debía permitirle crear una matriz de
RequestType
la manera que desee.
Podría pasar una matriz de esos tipos de solicitud a una función:
func handleQueueOfRequests(queue: [RequestType]) {
// frobulate All The Things!
for request in queue {
request.frobulateModel(/* What do I put here? */)
}
}
Llego al punto en que quiero frobular todas las cosas, pero necesito saber qué tipo de argumento pasar a la llamada.
Algunas de mis entidades
RequestType
podrían tomar un
LegoModel
, algunas podrían tomar un
PlasticModel
y otras podrían tomar un
PeanutButterAndPeepsModel
.
Swift no está contento con la ambigüedad, por lo que no le permitirá declarar una variable de un protocolo que tenga un tipo asociado.
Al mismo tiempo, tiene mucho sentido, por ejemplo, crear una matriz de
RequestType
cuando SABEMOS que todos usan el
LegoModel
.
Esto parece razonable, y lo es, pero necesitas alguna forma de expresarlo.
Una forma de hacerlo es crear una clase (o estructura, o enumeración) que asocie un tipo real con el nombre del tipo de Modelo abstracto:
class LegoRequestType: RequestType {
typealias Model = LegoModel
// Implement protocol requirements here
}
Ahora es completamente razonable declarar un conjunto de
LegoRequestType
porque si quisiéramos
frobulate
todos, sabemos que tendríamos que pasar un
LegoModel
cada vez.
Este matiz con los tipos asociados hace que cualquier protocolo que los use sea especial.
La biblioteca estándar de Swift tiene protocolos como este, especialmente
Collection
o
Sequence
.
Para permitirle crear una matriz de cosas que implementan el protocolo de
Collection
o un conjunto de cosas que implementan el protocolo de secuencia, la Biblioteca Estándar emplea una técnica llamada "borrado de tipo" para crear los tipos de estructura
AnyCollection<T>
o
AnySequence<T>
.
La técnica de borrado de tipo es bastante compleja de explicar en una respuesta de desbordamiento de pila, pero si busca en la web hay muchos artículos al respecto.
Puedo recomendar un video de Alex Gallagher sobre Protocolos con tipos asociados (PAT) en YouTube.
Un pequeño cambio en el diseño de su código podría hacerlo posible. Agregue un protocolo vacío de tipo no asociado en la parte superior de la jerarquía de protocolos. Me gusta esto...
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''
}
Otro ejemplo, con clases derivadas del protocolo RequestType, haciendo una cola y pasando la cola a una función para imprimir el tipo apropiado
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)