Llamada a la implementación predeterminada del protocolo desde el método regular

82

Me pregunto si es posible lograr tal cosa.
Tengo un patio de juegos como este:

protocol Foo {
    func testPrint()
}

extension Foo {
    func testPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        // Calling self or super go call default implementation
        self.testPrint()
        print("Call from struct")
    }
}


let sth = Bar()
sth.testPrint()

Puedo proporcionar una implementación predeterminada en, extensionpero ¿qué Barpasa si necesita todo lo que está en la implementación predeterminada más cosas adicionales?
De alguna manera es similar a llamar a super.métodos en classes para cumplir con el requisito de implementar todas las propiedades, etc. pero no veo posibilidad de lograr lo mismo con structs.

cojoj
fuente
Yo usaría Foo.testPrint(self)()- el problema es que falla debido a una falla de segmentación (probado tanto en 7.0 GM como en 7.1 beta)
Antonio
1
Eso es una construcción extraña has presentado 😯
cojoj
4
Cada método de instancia es un método de curry estático que toma una instancia como su primer parámetro
Antonio
Sin embargo, intenté eliminar la extensión y arroja el mismo error de segmentación. Probablemente no se supone que funcione con protocolos
Antonio
Hmmm, es una pena que tenga que repetirme en el código, mientras que esto podría solucionarse fácilmente mediante el uso de la implementación predeterminada ...
cojoj

Respuestas:

90

No sé si todavía está buscando una respuesta a esto, pero la forma de hacerlo es eliminar la función de la definición del protocolo, convertir su objeto Fooy luego llamar al método en él:

protocol Foo { 
    // func testPrint() <- comment this out or remove it
}

extension Foo {
    func testPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        print("Call from struct")
        (self as Foo).testPrint() // <- cast to Foo and you'll get the  default
                                  //    function defined in the extension
    }
}

Bar().testPrint()

// Output:    "Call from struct"
//            "Protocol extension call"

Por alguna razón, solo funciona si la función no se declara como parte del protocolo, sino que se define en una extensión del protocolo. Imagínate. Pero funciona.

Aaron Rasmussen
fuente
1
¡Oh Dios! ¡No puedo creer que te obliguen a quitarle la función al protocolo! buena respuesta, gracias!
SkyWalker
5
Esto me parece un error, no debería ser posible obtener una implementación de método diferente simplemente lanzando la misma instancia.
MANIAK_dobrii
15
En realidad, esto cambia drásticamente la semántica del protocolo + extensión. Si deja la declaración fuera del protocolo, obtendrá un envío estático cuando llame a la función en un tipo que se ajuste al protocolo; es por eso que puede transmitir y obtener la implementación de la extensión. Si agrega la declaración al protocolo, su llamada a la función se enviará dinámicamente .
Thorsten Karrer
2
Esto funciona solo si el Fooprotocolo no hereda de ningún otro protocolo.
iyuna
4
¿No conduce esto a un bucle infinito?
Stan Liu
9

Bueno, puede crear un tipo anidado que se ajuste al protocolo, instanciarlo y llamar al método en ese (no importa que no pueda acceder a los datos de su tipo, ya que la implementación dentro de la extensión del protocolo no puede hacer referencia a él de todos modos). Pero no es una solución que yo llamaría elegante.

struct Bar: Foo {
    func testPrint() {
        // Calling default implementation
        struct Dummy : Foo {}
        let dummy = Dummy()
        dummy.testPrint()
        print("Call from struct")
    }
}
Thorsten Karrer
fuente
1
Parece que es la única posibilidad en este momento (confirmado por Apple) ... Presentaré un radar de características para este, ya que puede ser útil 👌
cojoj
4

¡Gracias por la publicacion! Si coloca la definición de la función en el protocolo, cuando el objeto se convierte en el protocolo, solo ve la versión del objeto de la función y, dado que lo está llamando dentro de sí mismo, obtiene la nueva dirección de Apple ...

Probé una versión como esta:

import UIKit
protocol MyProc
{
}

protocol MyFuncProc
{
    func myFunc()
}

extension MyProc
{
    func myFunc()
    {
        print("Extension Version")
    }
}

struct MyStruct: MyProc, MyFuncProc
{
    func myFunc()
    {
        print("Structure Version")
        (self as MyProc).myFunc()
    }
}

(MyStruct() as MyFuncProc).myFunc()

Esto da una salida de:

Structure Version
Extension Version
Jim Malak
fuente
3

En caso de que su protocolo tiene associatedTypeo Selfrequisitos, entonces el molde no funcionará. Para solucionar este problema, cree una implementación predeterminada "sombra" a la que puedan llamar tanto la implementación predeterminada regular como la del tipo conforme.

protocol Foo { 
    associatedType Bar
}

extension Foo {
    func testPrint() {
        defaultTestPrint()
    }
}

fileprivate extension Foo { // keep this as private as possible
    func defaultTestPrint() {
        // default implementation
    }
}

struct Bar: Foo {
    func testPrint() {
        // specialized implementation
        defaultTestPrint()
    }
}
David James
fuente
No puedo estar más de acuerdo contigo. defaultXX()es mucho más expresivo y legible que otras respuestas.
DawnSong
Y creo que Amin Madani mejoró tu respuesta.
DawnSong
2

¿Qué opinas de esa forma de solucionar esto?

protocol Foo {
    func testPrint()
}

extension Foo {
    func testPrint() {
        defaultTestPrint()
    }

    func defaultTestPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        // Calling self or super go call default implementation
        defaultTestPrint()
        print("Call from struct")
    }
}


let sth = Bar()
sth.testPrint()
Amin Madani
fuente
¿Es esta una respuesta?
Anh Pham
@AnhPham eso es todo, solo otro método para realizar la funcionalidad predeterminada
Amin Madani
cuchilla y solución fácil
Stephan Januar
La respuesta más expresiva y flexible.
DawnSong