Cómo exigir que un protocolo solo pueda ser adoptado por una clase específica

91

Quiero este protocolo:

protocol AddsMoreCommands {
     /* ... */
}

solo para ser adoptado por clases que heredan de la clase UIViewController. Esta página me dice que puedo especificar que solo es adoptado por una clase (a diferencia de una estructura) escribiendo

protocol AddsMoreCommands: class {
}

pero no veo cómo exigir que solo sea adoptado por una clase en particular. Esa página luego habla de agregar wherecláusulas a las extensiones de protocolo para verificar la conformidad, pero tampoco veo cómo adaptar eso.

extension AddsMoreCommands where /* what */ {
}

¿Hay alguna forma de hacer esto? ¡Gracias!

emrys57
fuente

Respuestas:

115
protocol AddsMoreCommands: class {
    // Code
}

extension AddsMoreCommands where Self: UIViewController {
    // Code
}
Roee84
fuente
4
Casi lo tengo ... escribí en selflugar de Self:-( ¡Muchas gracias, eso funciona bien!
emrys57
Para mí, esto causa cierta extrañeza sintáctica cuando lo uso junto con el casting.
Chris Prince
3
Esto no funcionará si necesita incluir una propiedad en el protocolo, ya que las extensiones no pueden contener propiedades almacenadas.
calce el
1
Puede tener propiedades almacenadas, solo necesita usar esto: objc_getAssociatedObject (self, & KeyName) as? PropertyType
Michał Ziobro
Esto también requiere conversión cuando una declaración let / var tiene tipo, AddsMoreCommandspero un método al que se la pasa espera unUIViewController
GoatInTheMachine
80

Esto también se puede lograr sin una extensión:

protocol AddsMoreCommands: class where Self: UIViewController {
   // code
}

EDITADO 04/11/2017 : Como señaló Zig , esto parece generar una advertencia en Xcode 9.1. Actualmente hay un problema reportado en el proyecto Swift (SR-6265) para eliminar la advertencia, lo vigilaré y actualizaré la respuesta en consecuencia.

EDITADO 2018/09/29 : classes necesario si la variable que almacenará la instancia necesita ser débil (como un delegado). Si no necesita una variable débil, puede omitir classy simplemente escribir lo siguiente y no habrá ninguna advertencia:

protocol AddsMoreCommands where Self: UIViewController {
   // code
}
rgkobashi
fuente
5
Qué coincidencia es que hice clic en una pregunta de hace dos años y encontré una solución perfecta publicada hace una hora 😲
Oscar Apeland
Xcode 9.1 ahora me está dando una advertencia sobre este dicho: Restricción de diseño redundante 'Self': 'AnyObject'. Restricción de restricción de diseño 'Self': 'AnyObject' implícito aquí. Cambiar mi código al formato de la respuesta aceptada parece ser más bueno.
Zig
2
A partir de Xcode 9.1, los protocolos de solo clase ahora usan en AnyObjectlugar de class. protocol AddsMoreCommands: AnyObject where Self: UIViewController { // code }
dodgio
@dodgio sigue recibiendo la misma advertencia al usarAnyObject
rgkobashi
1
@ DávidPásztor tiene razón, sin embargo, si desea usarlo en un patrón estructural como la delegación, para poder debilitar la propiedad es necesario agregar ´class´ explícitamente :)
rgkobashi
48

Debido a un problema en la respuesta anterior, terminé con esta declaración:

protocol AddsMoreCommands where Self : UIViewController { 
    // protocol stuff here  
}

sin advertencias en Xcode 9.1

Massmaker
fuente
5
Corrígeme si me equivoco, pero el problema con esto sobre la solución anterior (que genera una advertencia en Xcode 9.1 y superior), ¿es que no puedes declarar al delegado como débil?
Kyle Goslan
Además, cuando se utiliza esta solución con Swift 4.1, necesito propiedades elenco de la AddsMoreCommandsa UIViewControllerla cual quería evitar ...
fl034
9
Para evitar el cambio de tipo, puede hacer esto:typealias AddsMoreCommandsViewController = UIViewController & AddsMoreCommands
plu
35

Ahora en Swift 5 puedes lograr esto:

protocol AddsMoreCommands: UIViewController {
     /* ... */
}

Muy útil.

Wojciech Kulik
fuente