used type only has generic constraint because associatedtype associated ios swift generics properties protocols

ios - associatedtype - can only be used as a generic constraint because it has self or associated type requirements



Propiedad rĂ¡pida que se ajusta a un protocolo y clase (4)

Encontré el mismo problema y también probé el enfoque genérico. Finalmente, el enfoque genérico rompió todo el diseño.

Después de volver a pensar en este problema, descubrí que un protocolo que no se puede usar para especificar completamente un tipo (en otras palabras, debe venir con información de tipo adicional, como un tipo de clase) es poco probable que sea completo. Además, aunque el estilo Objc de declarar ClassType<ProtocolType> es útil, no tiene en cuenta el beneficio de la abstracción proporcionada por el protocolo porque dicho protocolo realmente no eleva el nivel de abstracción. Además, si dicha declaración aparece en múltiples lugares, tiene que ser duplicada. Peor aún, si varias declaraciones de este tipo están interrelacionadas (posiblemente un solo objeto se pasará a su alrededor), el programa se vuelve frágil y difícil de mantener porque más adelante, si es necesario cambiar la declaración en un lugar, todas las declaraciones relacionadas deben ser cambiado también.

Solución

Si el caso de uso de una propiedad involucra tanto un protocolo (por ejemplo, ProtocolX ClassX ) como algunos aspectos de una clase (por ejemplo, ClassX ), se podría tener en cuenta el siguiente enfoque:

  1. Declare un protocolo adicional que hereda de ProtocolX con los requisitos adicionales de método / propiedad que ClassX satisface automáticamente. Al igual que en el ejemplo a continuación, un método y una propiedad son los requisitos adicionales, que UIViewController automáticamente.

    protocol CustomTableViewDelegate: UITableViewDelegate { var navigationController: UINavigationController? { get } func performSegueWithIdentifier(identifier: String, sender: AnyObject?) }

  2. Declare un protocolo adicional que hereda de ProtocolX con una propiedad adicional de solo lectura del tipo ClassX . Este enfoque no solo permite el uso de ClassX en su totalidad, sino que también muestra la flexibilidad de no requerir una implementación para la subclase ClassX . Por ejemplo:

    protocol CustomTableViewDelegate: UITableViewDelegate { var viewController: UIViewController { get } } // Implementation A class CustomViewController: UIViewController, UITableViewDelegate { var viewController: UIViewController { return self } ... // Other important implementation } // Implementation B class CustomClass: UITableViewDelegate { private var _aViewControllerRef: UIViewController // Could come from anywhere e.g. initializer var viewController: UIViewController { return _aViewControllerRef } ... // UITableViewDelegate methods implementation }

PD. Los fragmentos de UIViewController anteriores son solo para demostración, no se recomienda mezclar UIViewController y UITableViewDelegate juntos.

Editar para Swift 2+: Gracias por el comentario de @Shaps, se podría agregar lo siguiente para evitar tener que implementar la propiedad deseada en todas partes.

extension CustomTableViewDelegate where Self: UIViewController { var viewController: UIViewController { return self } }

@property (strong, nonatomic) UIViewController<UITableViewDelegate> *thing;

Quiero implementar una propiedad como en este código de Objective-C en Swift. Así que aquí está lo que he intentado:

class AClass<T: UIViewController where T: UITableViewDelegate>: UIViewController { var thing: T! }

Esto compila. Mi problema viene cuando agrego propiedades desde el guión gráfico. La etiqueta @IBOutlet genera un error de compilación.

class AClass<T: UIViewController where T: UITableViewDelegate>: UIViewController { @IBOutlet weak var anotherThing: UILabel! // error var thing: T! }

El error:

Variable in a generic class cannot be represented in Objective-C

¿Estoy implementando este derecho? ¿Qué puedo hacer para solucionar o solucionar este error?

EDITAR:

Swift 4 finalmente tiene una solución para este problema. Ver mi respuesta actualizada.


No es la solución ideal, pero puedes usar una función genérica en lugar de una clase genérica, como esta:

class AClass: UIViewController { @IBOutlet weak var anotherThing: UILabel! private var thing: UIViewController? func setThing<T: UIViewController where T: UITableViewDelegate>(delegate: T) { thing = delegate } }


Puedes declarar un delegado en Swift así:

weak var delegate : UITableViewDelegate?

Funcionará incluso con proyectos híbridos (Objective-c y swift). El delegado debe ser opcional y débil porque su disponibilidad no está garantizada y débil no crea un ciclo de retención.

Está recibiendo ese error porque no hay genéricos en Objective-C y no le permitirá agregar la propiedad @IBOutlet.

Editar: 1. Forzar un tipo en delegado

Para forzar que el delegado siempre sea un UIViewController, puede implementar el configurador personalizado y lanzar una excepción cuando no sea un UIViewController.

weak var _delegate : UITableViewDelegate? //stored property var delegate : UITableViewDelegate? { set { if newValue! is UIViewController { _delegate = newValue } else { NSException(name: "Inavlid delegate type", reason: "Delegate must be a UIViewController", userInfo: nil).raise() } } get { return _delegate } }


Actualización para Swift 4

Swift 4 ha agregado soporte para representar un tipo como una clase que se ajusta a un protocolo. La sintaxis es Class & Protocol . Aquí hay un código de ejemplo que usa este concepto de "What''s New in Swift" (sesión 402 de WWDC 2017):

protocol Shakeable { func shake() } extension UIButton: Shakeable { /* ... */ } extension UISlider: Shakeable { /* ... */ } // Example function to generically shake some control elements func shakeEm(controls: [UIControl & Shakeable]) { for control in controls where control.isEnabled { control.shake() } }

A partir de Swift 3, este método causa problemas porque no puede pasar los tipos correctos. Si intentas pasar a [UIControl] , no tiene el método shake . Si intentas pasar [UIButton] , entonces el código se compila, pero no puedes pasar ningún UISlider s. Si pasa en [Shakeable] , entonces no puede verificar control.state , porque Shakeable no tiene eso. Swift 4 finalmente abordó el tema.

Respuesta antigua

Estoy solucionando este problema por el momento con el siguiente código:

// This class is used to replace the UIViewController<UITableViewDelegate> // declaration in Objective-C class ConformingClass: UIViewController, UITableViewDelegate {} class AClass: UIViewController { @IBOutlet weak var anotherThing: UILabel! var thing: ConformingClass! }

Esto me parece hacker. Si se requiriera alguno de los métodos de delegado, tendría que implementar esos métodos en ConformingClass (que NO quiero hacer) y anularlos en una subclase.

He publicado esta respuesta en caso de que alguien más encuentre este problema y mi solución los ayude, pero no estoy satisfecho con la solución. Si alguien publica una mejor solución, aceptaré su respuesta.