Encontré el siguiente código en SR-142 en bugs.swift.org .
Si un protocolo tiene un método de extensión que está mutando, una instancia de clase puede llamar a la función de mutación sin ningún problema.
// protocol definition protocol P { } extension P { mutating func m() { } } // class conforming to P class C : P { // redeclare m() without the mutating qualifier func m() { // call protocol's default implementation var p: P = self pm() } } let c = C() cm()Si hago un pequeño cambio para agregar el método a la declaración del protocolo:
protocol P { mutating func m() // This is what I added. } extension P { mutating func m() { } } class C : P { func m() { var p: P = self pm() } } let c = C() cm() // This one is calling itself indefinitely; why? ¿Por qué cm() sigue llamándose a sí mismo una y otra vez?
Con su cambio en el segundo ejemplo, al incluir la m en la definición del protocolo, eso le indica a Swift que emplee despacho dinámico. Entonces, cuando llama a pm() , determina dinámicamente si el objeto ha anulado la implementación predeterminada del método. En este ejemplo particular, eso da como resultado que el método se llame recursivamente a sí mismo.
Pero en el primer ejemplo, a falta de que el método forme parte de la definición del protocolo, Swift empleará el envío estático y, dado que p es del tipo P , llamará a la implementación m en P
A modo de ejemplo, considere dónde el método no forma parte de la definición del protocolo (y, por lo tanto, no está en la "tabla de testigos del protocolo"):
protocol P { // func method() } extension P { func method() { print("Protocol default implementation") } } struct Foo: P { func method() { print(“Foo implementation") } } Debido a que foo es una referencia P y porque el method no es parte de la definición P , excluye el method de la tabla testigo del protocolo y emplea el envío estático. Como resultado, lo siguiente imprimirá “Implementación predeterminada del protocolo”:
let foo: P = Foo() foo.method() // Protocol default implementation Pero si cambia el protocolo para incluir explícitamente este método, dejando todo lo demás igual, el method se incluirá en la tabla testigo del protocolo:
protocol P { func method() } Luego, lo siguiente ahora imprimirá "Implementación de Foo", porque aunque la variable foo es de tipo P , determinará dinámicamente si el tipo subyacente, Foo , ha anulado ese método:
let foo: P = Foo() foo.method() // Foo implementationPara obtener más información sobre el envío dinámico frente al estático, consulte el video WWDC 2016Comprender el rendimiento de Swift .
Al declarar m en el protocolo y proporcionar la implementación en su clase, sobrescribe la implementación predeterminada.
Pero en el primer ejemplo, cuando lanza su clase como protocolo, llamará a la implementación predeterminada del protocolo porque la implementación de la clase es propia y no sobrescribe ninguno de los métodos del protocolo.