Finalmente me motivé a actualizar mi respuesta, puedes reconsiderar aceptarla.
akashivskyy
@akashivskyy Acepto su respuesta porque muestra mejor todas las opciones disponibles y sus ventajas y desventajas.
Selvin
Respuestas:
506
1. Uso de implementaciones predeterminadas (preferidas).
protocolMyProtocol{func doSomething()}extensionMyProtocol{func doSomething(){/* return a default value or just leave empty */}}structMyStruct:MyProtocol{/* no compile error */}
Ventajas
No hay tiempo de ejecución de Objective-C involucrado (bueno, al menos no explícitamente). Esto significa que puede conformar estructuras, enumeraciones y no NSObjectclases. Además, esto significa que puede aprovechar el potente sistema de genéricos.
Siempre puede estar seguro de que se cumplen todos los requisitos al encontrar tipos que se ajustan a dicho protocolo. Siempre es una implementación concreta o una predeterminada. Así es como se comportan las "interfaces" o los "contratos" en otros idiomas.
Desventajas
Para los no Voidrequisitos, debe tener un valor predeterminado razonable , que no siempre es posible. Sin embargo, cuando encuentre este problema, significa que tal requisito realmente no debería tener una implementación predeterminada o que cometió un error durante el diseño de la API.
No puede distinguir entre una implementación predeterminada y ninguna implementación , al menos sin abordar ese problema con valores de retorno especiales. Considere el siguiente ejemplo:
Si proporciona una implementación predeterminada que solo regresa true, está bien a primera vista. Ahora, considere el siguiente pseudocódigo:
finalclassSomeParser{func parse(data:Data)->[Any]{if/* delegate.validate(value:) is not implemented */{/* parse very fast without validating */}else{/* parse and validate every value */}}}
No hay forma de implementar tal optimización: no puede saber si su delegado implementa un método o no.
Aunque hay varias maneras diferentes de superar este problema (usando cierres opcionales, diferentes objetos delegados para diferentes operaciones, por nombrar algunos), ese ejemplo presenta el problema claramente.
2. Utilizando @objc optional.
@objc protocolMyProtocol{@objc optionalfunc doSomething()}classMyClass:NSObject,MyProtocol{/* no compile error */}
Ventajas
No se necesita una implementación predeterminada. Simplemente declaras un método opcional o una variable y estás listo para comenzar.
Desventajas
Limita severamente las capacidades de su protocolo al requerir que todos los tipos conformes sean compatibles con Objective-C. Esto significa que solo las clases que heredan de NSObjectpueden ajustarse a dicho protocolo. Sin estructuras, sin enumeraciones, sin tipos asociados.
Siempre debe verificar si se implementa un método opcional llamando o verificando opcionalmente si el tipo conforme lo implementa. Esto podría introducir una gran cantidad de repeticiones si llama a métodos opcionales con frecuencia.
Mire la forma mejorada de métodos opcionales en Swift usando una extensión (abajo)
Daniel Kanaan
1
¿Y cómo se prueba la compatibilidad de un método de protocolo opcional en una instancia? respondsToSelector?
devios1
3
¡Simplemente no hagas esto! Swift no admite el método opcional en protocolos por una razón.
fpg1503
2
Este método no admite opciones en parámetros. Es decir, no puedes hacer esooptional func doSomething(param: Int?)
SoftDesigner
44
Seguramente esta respuesta es esencialmente incorrecta hoy en día, años después. Hoy en Swift simplemente agrega una extensión para la implementación predeterminada, es un aspecto básico de Swift. (Como muestran todas las respuestas modernas a continuación.) Sería un error agregar la bandera objc, hoy en día.
Fattie
394
En Swift 2 y en adelante, es posible agregar implementaciones predeterminadas de un protocolo. Esto crea una nueva forma de métodos opcionales en los protocolos.
protocolMyProtocol{func doSomethingNonOptionalMethod()func doSomethingOptionalMethod()}extensionMyProtocol{func doSomethingOptionalMethod(){// leaving this empty
}}
No es una forma realmente agradable de crear métodos de protocolo opcionales, pero le brinda la posibilidad de usar estructuras en devoluciones de llamada de protocolo.
Esta es probablemente la forma más limpia de hacerlo en Swift. Lástima que no funcione antes de Swift 2.0.
Entalpi
13
@MattQuiros Estoy descubriendo que, de hecho, debe declarar la función en la definición del protocolo, de lo contrario, la función de extensión no operativa no se anula en sus clases que se ajustan al protocolo.
Ian Pearce
3
@IanPearce es correcto, y esto parece ser por diseño. En la charla "Programación orientada al protocolo" (408) en WWDC, hablan sobre métodos en el protocolo principal que son "puntos de personalización" ofrecidos a los tipos conformes. Un punto de personalización requerido no recibe una definición en una extensión; un punto opcional hace. Los métodos en el protocolo que generalmente no deben personalizarse se declaran / definen por completo en la extensión para no permitir que los tipos conformes se personalicen a menos que específicamente se reduzca a su tipo dinámico para mostrar que desea la implementación personalizada del conformador.
Matthias
44
@FranklinYu Puede hacer esto, pero luego está contaminando su diseño de API con 'tiros' donde de hecho no es necesario. Me gusta más la idea de "micro protocolos". Por ejemplo, cada método confirma un protocolo y luego puede verificar si el objeto es protocolo
Darko
3
@Antoine Creo que deberías hacer pública la función en la extensión, ya que las funciones en los protocolos son públicas por definición. Su solución no funcionará cuando el protocolo se use fuera de su módulo.
Vadim Eisenberg
39
Dado que hay algunas respuestas sobre cómo usar el modificador opcional y el atributo @objc para definir el protocolo de requisitos opcionales, daré una muestra sobre cómo usar las extensiones de protocolo para definir el protocolo opcional.
El siguiente código es Swift 3. *.
/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocolCancelable{/// default implementation is empty.
func cancel()}extensionCancelable{func cancel(){}}classPlane:Cancelable{//Since cancel() have default implementation, that is optional to class Plane
}let plane =Plane()
plane.cancel()//Print out *UnitedAirlines can't cancelable*
Tenga en cuenta que los métodos de extensión de protocolo no pueden ser invocados por el código Objective-C, y lo peor es que el equipo Swift no lo solucionará. https://bugs.swift.org/browse/SR-492
¡Buen trabajo! Esta debería ser la respuesta correcta ya que resuelve el problema sin involucrar el tiempo de ejecución de Objective-C.
Lukas
realmente agradable!
tania_S
de la documentación rápida: "Los requisitos de protocolo con implementaciones predeterminadas proporcionadas por extensiones son distintos de los requisitos de protocolo opcionales. Aunque los tipos conformes no tienen que proporcionar su propia implementación, los requisitos con implementaciones predeterminadas se pueden invocar sin encadenamiento opcional".
Mec Os
34
Las otras respuestas aquí que implican marcar el protocolo como "@objc" no funcionan cuando se usan tipos específicos de swift.
structInfo{var height:Intvar weight:Int}@objc protocolHealth{func isInfoHealthy(info:Info)->Bool}//Error"Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"
Para declarar protocolos opcionales que funcionen bien con swift, declare las funciones como variables en lugar de funciones.
Y luego implemente el protocolo de la siguiente manera
classHuman:Health{var isInfoHealthy:(Info)->(Bool)?={ info inif info.weight <200&& info.height >72{returntrue}returnfalse}//Or leave out the implementation and declare it as:
//var isInfoHealthy: (Info) -> (Bool)?
}
Entonces puedes usar "?" para verificar si la función se ha implementado o no
Solo las clases, protocolos, métodos y propiedades pueden usar @objc. En caso de que esté utilizando un parámetro Enum en la definición del método de protocolo @ objc, está condenado.
Khunshan
1
@khunshan, este método no requiere que se marque nada @ objc, ¿a qué se refiere?
Zag
es una información sobre el tema de que las enumeraciones no se pueden usar entre swift y objc y que las otras declaraciones se pueden unir con la palabra clave @objc.
Khunshan
34
Aquí hay un ejemplo concreto con el patrón de delegación.
Creo que antes de preguntar cómo puede implementar un método de protocolo opcional, debería preguntarse por qué debería implementar uno.
Si pensamos en los protocolos rápidos como una interfaz en la programación orientada a objetos clásica, los métodos opcionales no tienen mucho sentido, y tal vez una mejor solución sería crear una implementación predeterminada o separar el protocolo en un conjunto de protocolos (tal vez con algunas relaciones de herencia entre ellos) para representar la posible combinación de métodos en el protocolo.
Aquí hay un ejemplo muy simple para clases rápidas SOLAMENTE, y no para estructuras o enumeraciones. Tenga en cuenta que el método de protocolo es opcional, tiene dos niveles de encadenamiento opcional en juego. Además, la clase que adopta el protocolo necesita el atributo @objc en su declaración.
@objc protocolCollectionOfDataDelegate{optionalfunc indexDidChange(index:Int)}@objc classRootView:CollectionOfDataDelegate{var data =CollectionOfData()init(){
data.delegate =self
data.indexIsNow()}func indexDidChange(index:Int){
println("The index is currently: \(index)")}}classCollectionOfData{var index :Int?weakvar delegate :CollectionOfDataDelegate?func indexIsNow(){
index =23
delegate?.indexDidChange?(index!)}}
¿Puedes describir un poco más la parte de " dos niveles de juego opcional ", a saber, esto delegate?.indexDidChange?(index!):?
Unheilig
2
Si hubiéramos escrito el protocolo para tener un método no opcional como este: protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) } entonces lo llamaría sin el signo de interrogación: delegate?.indexDidChange(index!) cuando establece un requisito opcional para un método en un protocolo, el Tipo que se ajuste a él NO podría implementar ese método , por lo que ?se utiliza para verificar la implementación y, si no hay ninguna, el programa no se bloqueará. @Unheilig
Bendición Lopes
weak var delegate : CollectionOfDataDelegate?(¿asegurar una referencia débil?)
Alfie Hanssen
@BlessingLopes ¿Puedes agregar tu explicación del delegate?uso a tu respuesta? Esa información realmente debería pertenecer a otros en el futuro. Quiero votar esto, pero esa información realmente debería estar en la respuesta.
Johnathon Sullinger
3
si desea hacerlo de manera rápida, la mejor manera es proporcionar una implementación predeterminada particularmente si devuelve un tipo Swift como, por ejemplo, struct con tipos Swift
Hay dos formas de crear un método opcional en un protocolo rápido.
1 - La primera opción es marcar su protocolo usando el atributo @objc. Si bien esto significa que solo puede ser adoptado por las clases, significa que marca métodos individuales como opcionales como este:
2 - Una forma más rápida: esta opción es mejor. Escriba implementaciones predeterminadas de los métodos opcionales que no hacen nada, como este.
protocolMyProtocol{func optionalMethod()func notOptionalMethod()}extensionMyProtocol{func optionalMethod(){//this is a empty implementation to allow this method to be optional
}}
Swift tiene una característica llamada extensión que nos permite proporcionar una implementación predeterminada para aquellos métodos que queremos que sean opcionales.
Defina la función en el protocolo y cree una extensión para ese protocolo, luego cree una implementación vacía para la función que desea usar como opcional.
Para definir OptionalProtocolrápidamente, debe usar la @objcpalabra clave antes de la Protocoldeclaración y attribute/ methoddeclaración dentro de ese protocolo. A continuación se muestra una muestra de la propiedad opcional de un protocolo.
@objc protocolProtocol{@objc optionalvar name:String?}classMyClass:Protocol{// No error
}
Si bien esto puede responder la pregunta, es mejor agregar alguna descripción sobre cómo esta respuesta puede ayudar a resolver el problema. Lea Cómo escribo una buena respuesta para saber más.
Respuestas:
1. Uso de implementaciones predeterminadas (preferidas).
Ventajas
No hay tiempo de ejecución de Objective-C involucrado (bueno, al menos no explícitamente). Esto significa que puede conformar estructuras, enumeraciones y no
NSObject
clases. Además, esto significa que puede aprovechar el potente sistema de genéricos.Siempre puede estar seguro de que se cumplen todos los requisitos al encontrar tipos que se ajustan a dicho protocolo. Siempre es una implementación concreta o una predeterminada. Así es como se comportan las "interfaces" o los "contratos" en otros idiomas.
Desventajas
Para los no
Void
requisitos, debe tener un valor predeterminado razonable , que no siempre es posible. Sin embargo, cuando encuentre este problema, significa que tal requisito realmente no debería tener una implementación predeterminada o que cometió un error durante el diseño de la API.No puede distinguir entre una implementación predeterminada y ninguna implementación , al menos sin abordar ese problema con valores de retorno especiales. Considere el siguiente ejemplo:
Si proporciona una implementación predeterminada que solo regresa
true
, está bien a primera vista. Ahora, considere el siguiente pseudocódigo:No hay forma de implementar tal optimización: no puede saber si su delegado implementa un método o no.
Aunque hay varias maneras diferentes de superar este problema (usando cierres opcionales, diferentes objetos delegados para diferentes operaciones, por nombrar algunos), ese ejemplo presenta el problema claramente.
2. Utilizando
@objc optional
.Ventajas
Desventajas
Limita severamente las capacidades de su protocolo al requerir que todos los tipos conformes sean compatibles con Objective-C. Esto significa que solo las clases que heredan de
NSObject
pueden ajustarse a dicho protocolo. Sin estructuras, sin enumeraciones, sin tipos asociados.Siempre debe verificar si se implementa un método opcional llamando o verificando opcionalmente si el tipo conforme lo implementa. Esto podría introducir una gran cantidad de repeticiones si llama a métodos opcionales con frecuencia.
fuente
respondsToSelector
?optional func doSomething(param: Int?)
En Swift 2 y en adelante, es posible agregar implementaciones predeterminadas de un protocolo. Esto crea una nueva forma de métodos opcionales en los protocolos.
No es una forma realmente agradable de crear métodos de protocolo opcionales, pero le brinda la posibilidad de usar estructuras en devoluciones de llamada de protocolo.
Escribí un pequeño resumen aquí: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
fuente
Dado que hay algunas respuestas sobre cómo usar el modificador opcional y el atributo @objc para definir el protocolo de requisitos opcionales, daré una muestra sobre cómo usar las extensiones de protocolo para definir el protocolo opcional.
El siguiente código es Swift 3. *.
Tenga en cuenta que los métodos de extensión de protocolo no pueden ser invocados por el código Objective-C, y lo peor es que el equipo Swift no lo solucionará. https://bugs.swift.org/browse/SR-492
fuente
Las otras respuestas aquí que implican marcar el protocolo como "@objc" no funcionan cuando se usan tipos específicos de swift.
Para declarar protocolos opcionales que funcionen bien con swift, declare las funciones como variables en lugar de funciones.
Y luego implemente el protocolo de la siguiente manera
Entonces puedes usar "?" para verificar si la función se ha implementado o no
fuente
Aquí hay un ejemplo concreto con el patrón de delegación.
Configure su protocolo:
Establezca el delegado en una clase e implemente el Protocolo. Ver que el método opcional no necesita ser implementado.
Una cosa importante es que el método opcional es opcional y necesita un "?" al llamar Mencione el segundo signo de interrogación.
fuente
En Swift 3.0
Te ahorrará tiempo.
fuente
@objc
se necesita en todos los miembros ahora?required
flag, pero con errores:required
solo se puede usar en declaraciones 'init'.optional
palabra clave antes de cada método.fuente
@objc
, no solo el protocolo.Un enfoque puro de Swift con herencia de protocolo:
fuente
Para ilustrar la mecánica de la respuesta de Antoine:
fuente
Creo que antes de preguntar cómo puede implementar un método de protocolo opcional, debería preguntarse por qué debería implementar uno.
Si pensamos en los protocolos rápidos como una interfaz en la programación orientada a objetos clásica, los métodos opcionales no tienen mucho sentido, y tal vez una mejor solución sería crear una implementación predeterminada o separar el protocolo en un conjunto de protocolos (tal vez con algunas relaciones de herencia entre ellos) para representar la posible combinación de métodos en el protocolo.
Para más información, consulte https://useyourloaf.com/blog/swift-optional-protocol-methods/ , que ofrece una excelente descripción general sobre este asunto.
fuente
Ligeramente fuera del tema de la pregunta original, pero se basa en la idea de Antoine y pensé que podría ayudar a alguien.
También puede hacer que las propiedades calculadas sean opcionales para estructuras con extensiones de protocolo.
Puede hacer que una propiedad en el protocolo sea opcional
Implemente la propiedad calculada ficticia en la extensión de protocolo
Y ahora puede usar estructuras que tienen o no implementada la propiedad opcional
También he escrito cómo hacer propiedades opcionales en los protocolos Swift en mi blog , que mantendré actualizado en caso de que las cosas cambien a través de las versiones de Swift 2.
fuente
Cómo crear métodos delegados opcionales y necesarios.
fuente
Aquí hay un ejemplo muy simple para clases rápidas SOLAMENTE, y no para estructuras o enumeraciones. Tenga en cuenta que el método de protocolo es opcional, tiene dos niveles de encadenamiento opcional en juego. Además, la clase que adopta el protocolo necesita el atributo @objc en su declaración.
fuente
delegate?.indexDidChange?(index!)
:?protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) }
entonces lo llamaría sin el signo de interrogación:delegate?.indexDidChange(index!)
cuando establece un requisito opcional para un método en un protocolo, el Tipo que se ajuste a él NO podría implementar ese método , por lo que?
se utiliza para verificar la implementación y, si no hay ninguna, el programa no se bloqueará. @Unheiligweak var delegate : CollectionOfDataDelegate?
(¿asegurar una referencia débil?)delegate?
uso a tu respuesta? Esa información realmente debería pertenecer a otros en el futuro. Quiero votar esto, pero esa información realmente debería estar en la respuesta.si desea hacerlo de manera rápida, la mejor manera es proporcionar una implementación predeterminada particularmente si devuelve un tipo Swift como, por ejemplo, struct con tipos Swift
ejemplo:
entonces puede implementar el protocolo sin definir cada función
fuente
Hay dos formas de crear un método opcional en un protocolo rápido.
1 - La primera opción es marcar su protocolo usando el atributo @objc. Si bien esto significa que solo puede ser adoptado por las clases, significa que marca métodos individuales como opcionales como este:
2 - Una forma más rápida: esta opción es mejor. Escriba implementaciones predeterminadas de los métodos opcionales que no hacen nada, como este.
Swift tiene una característica llamada extensión que nos permite proporcionar una implementación predeterminada para aquellos métodos que queremos que sean opcionales.
fuente
Una opción es almacenarlos como variables de función opcionales:
fuente
Defina la función en el protocolo y cree una extensión para ese protocolo, luego cree una implementación vacía para la función que desea usar como opcional.
fuente
Para definir
Optional
Protocol
rápidamente, debe usar la@objc
palabra clave antes de laProtocol
declaración yattribute
/method
declaración dentro de ese protocolo. A continuación se muestra una muestra de la propiedad opcional de un protocolo.fuente
Poner
@optional
delante de métodos o propiedades.fuente
@optional
Ni siquiera es la palabra clave correcta. Esoptional
, y debe declarar la clase y el protocolo con el@objc
atributo.