Aufruf der Standardimplementierung des Protokolls über die reguläre Methode

81

Ich frage mich, ob es möglich ist, so etwas zu erreichen.
Ich habe einen Spielplatz wie diesen:

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()

Ich kann eine Standardimplementierung in bereitstellen, extensionaber was ist, wenn Baralles benötigt wird, was in der Standardimplementierung enthalten ist, sowie zusätzliche Dinge?
Es ist irgendwie ähnlich wie das Aufrufen von super.Methoden in classes, um die Anforderung der Implementierung jeder Eigenschaft usw. zu erfüllen, aber ich sehe keine Möglichkeit, dasselbe mit zu erreichen structs.

cojoj
quelle
Ich würde verwenden Foo.testPrint(self)()- das Problem ist, dass es aufgrund eines Segmentierungsfehlers (getestet auf 7.0 GM und 7.1 Beta) fehlschlägt
Antonio
1
Das ist eine seltsame Konstruktion, die Sie vorgestellt haben co
cojoj
4
Jede Instanzmethode ist eine statische Curry-Methode, die eine Instanz als ersten Parameter verwendet
Antonio
Ich habe jedoch versucht, die Erweiterung zu entfernen, und es wird der gleiche Segmentierungsfehler ausgegeben. Wahrscheinlich soll das nicht mit Protokollen funktionieren
Antonio
Hmmm, schade, dass ich mich im Code wiederholen muss, während dies mit der Standardimplementierung leicht behoben werden kann ...
cojoj

Antworten:

89

Ich weiß nicht, ob Sie noch nach einer Antwort darauf suchen, aber Sie können die Funktion aus der Protokolldefinition entfernen, Ihr Objekt umwandeln Foound dann die Methode darauf aufrufen:

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"

Aus irgendeinem Grund funktioniert es nur, wenn die Funktion nicht als Teil des Protokolls deklariert ist, sondern in einer Erweiterung des Protokolls definiert ist. Stelle dir das vor. Aber es funktioniert.

Aaron Rasmussen
quelle
1
Oh Gott! Ich kann nicht glauben, dass sie dich zwingen, die Funktion aus dem Protokoll zu entfernen! gute antwort, danke!
SkyWalker
5
Dies sieht für mich nach einem Fehler aus. Es sollte nicht möglich sein, eine andere Methodenimplementierung zu erhalten, indem nur dieselbe Instanz gecastet wird.
MANIAK_dobrii
15
Dies ändert die Semantik der Protokoll + -Erweiterung drastisch. Wenn Sie die Deklaration aus dem Protokoll herauslassen, erhalten Sie einen statischen Versand, wenn Sie die Funktion für einen Typ aufrufen, der dem Protokoll entspricht. Aus diesem Grund können Sie die Implementierung über die Erweiterung umwandeln und abrufen. Wenn Sie die Deklaration zum Protokoll hinzufügen, wird Ihr Aufruf der Funktion dynamisch ausgelöst.
Thorsten Karrer
2
Dies funktioniert jedoch nur, wenn das FooProtokoll kein anderes Protokoll erbt.
Iyuna
4
Führt dies nicht zu einer Endlosschleife?
Stan Liu
9

Nun, Sie könnten einen verschachtelten Typ erstellen, der dem Protokoll entspricht, ihn instanziieren und die Methode für diesen aufrufen (es spielt keine Rolle, dass Sie nicht auf die Daten Ihres Typs zugreifen können, da die Implementierung in der Protokollerweiterung sowieso nicht darauf verweisen kann). Aber es ist keine Lösung, die ich als elegant bezeichnen würde.

struct Bar: Foo {
    func testPrint() {
        // Calling default implementation
        struct Dummy : Foo {}
        let dummy = Dummy()
        dummy.testPrint()
        print("Call from struct")
    }
}
Thorsten Karrer
quelle
1
Es sieht so aus, als ob es momentan die einzige Möglichkeit ist (von Apple bestätigt) ... Ich werde ein Feature-Radar für dieses einreichen, da es nützlich sein kann 👌
cojoj
4

Danke für den Beitrag! Wenn Sie die Funktionsdefinition in das Protokoll einfügen, wird beim Umwandeln des Objekts als Protokoll nur die Version der Funktion des Objekts angezeigt, und da Sie es in sich selbst aufrufen, erhalten Sie die neue Adresse von Apple ...

Ich habe eine Version wie diese ausprobiert:

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()

Dies ergibt eine Ausgabe von:

Structure Version
Extension Version
Jim Malak
quelle
3

Wenn Ihr Protokoll hat associatedTypeoder SelfAnforderungen, dann wird die Besetzung nicht. Um dies zu umgehen, erstellen Sie eine "Schatten" -Standardimplementierung, die sowohl von der regulären Standardimplementierung als auch vom konformen Typ aufgerufen werden kann.

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
quelle
Kann dir nicht mehr zustimmen. defaultXX()ist viel ausdrucksvoller und lesbarer als andere Antworten.
DawnSong
Und ich denke, Amin Madani hat Ihre Antwort verbessert.
DawnSong
2

Was denkst du über eine solche Art, dies zu beheben?

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
quelle
Ist das eine Antwort?
Anh Pham
@AnhPham das ist es, nur eine andere Methode, um Standardfunktionalität auszuführen
Amin Madani
Hackmesser und einfache Lösung
Stephan Januar
Die ausdrucksstärkste und flexibelste Antwort.
DawnSong