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!

Respuestas:


115
protocol AddsMoreCommands: class {
    // Code
}

extension AddsMoreCommands where Self: UIViewController {
    // Code
}

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
}

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


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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.