uso usan son que para las etiquetas etiqueta ios swift generics swift2 swift-protocols

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)