Swift - Klassenmethode, die von der Unterklasse überschrieben werden muss


91

Gibt es eine Standardmethode, um in Swift eine "reine virtuelle Funktion" zu erstellen, d. H. einer, der muss von jeder Unterklasse außer Kraft gesetzt werden, und die, wenn sie es nicht, eine Kompilierung Fehler verursacht ist?


Sie könnten es in der Superklasse implementieren und eine Aussage machen. Ich habe gesehen, dass dies in Obj-C, Java und Python verwendet wird.
David Skrundz

8
@NSArray Dies verursacht einen Laufzeit- und keinen Kompilierungsfehler
JuJoDi

Diese Antwort wird Ihnen auch helfen.
Geben Sie

Eine reine virtuelle Funktion wird von protocols implementiert (im Vergleich zu interfaces in Java). Wenn Sie sie wie abstrakte Methoden verwenden müssen, schauen Sie sich diese Frage / Antwort an: stackoverflow.com/a/39038828/2435872
jboi

Antworten:


146

Sie haben zwei Möglichkeiten:

1. Verwenden Sie ein Protokoll

Definieren Sie die Oberklasse als Protokoll anstelle einer Klasse

Pro : Überprüfung der Kompilierungszeit, um festzustellen, ob jede "Unterklasse" (keine tatsächliche Unterklasse) die erforderlichen Methoden implementiert.

Con : Die "Superklasse" (Protokoll) kann keine Methoden oder Eigenschaften implementieren

2. Bestätigen Sie in der Super-Version der Methode

Beispiel:

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

Pro : Kann Methoden und Eigenschaften in der Oberklasse implementieren

Con : Keine Überprüfung der Kompilierungszeit


3
@jewirth Sie würden immer noch keine Überprüfung der Kompilierungszeit für die Unterklassen bekommen
drawag

5
Das Protokoll kann keine Methoden implementieren, aber Sie können sie stattdessen über Erweiterungsmethoden bereitstellen.
David Moles

2
Ab Swift 2.0 gibt es jetzt auch Protokollerweiterungen :) Apple Reference .
Ephemera

4
Obwohl fatalErrorkeine Überprüfung der Kompilierungszeit möglich ist, ist es schön, dass der Compiler zumindest so intelligent ist, dass Sie beim Aufrufen des Ausführungspfads keinen Rückgabewert für die Methode angeben müssen fatalError.
Bugloaf

3
Fall 2: Beachten Sie, dass beim Aufrufen super.someFunc()der überschriebenen Methode der Fehler angezeigt wird, obwohl Sie ihn überschrieben haben. Sie wissen, dass Sie es nicht nennen sollen, aber jemand anderes muss das nicht wissen und einfach der Standardpraxis folgen.
Jakub Truhlář

45

Im Folgenden können Sie von einer Klasse erben und die Kompilierungszeit des Protokolls überprüfen lassen :)

protocol ViewControllerProtocol {
    func setupViews()
    func setupConstraints()
}

typealias ViewController = ViewControllerClass & ViewControllerProtocol

class ViewControllerClass : UIViewController {

    override func viewDidLoad() {
        self.setup()
    }

    func setup() {
        guard let controller = self as? ViewController else {
            return
        }

        controller.setupViews()
        controller.setupConstraints()
    }

    //.... and implement methods related to UIViewController at will

}

class SubClass : ViewController {

    //-- in case these aren't here... an error will be presented
    func setupViews() { ... }
    func setupConstraints() { ... }

}

2
nett, typealias zur Rettung :)
Chris Allinson

Gibt es eine Möglichkeit, Benutzer dieser API daran zu hindern, ihre Clild-Klassen von ViewControllerClass anstatt von ViewController abzuleiten? Dies ist eine großartige Lösung für mich, da ich in einigen Jahren von meinem Typ-Alias ​​ableiten werde und vergessen habe, welche Funktionen bis dahin überschrieben werden müssen.
David Rector

@ David Rector, können Sie Ihre Klasse privat und Ihre Typealien öffentlich machen? Entschuldigung, Nachrichten von meinem Telefon, kann mich nicht überprüfen.
ScottyBlades

1
Perfekte Lösung, danke dafür. Wie von @DavidRector unterstrichen, wäre es großartig, wenn es eine Lösung gäbe, die es auch so macht, dass nur die Typealien öffentlich sind, aber es scheint leider nicht möglich zu sein.
CyberDandy

35

Es gibt keine Unterstützung für abstrakte Klassen- / virtuelle Funktionen, aber Sie könnten wahrscheinlich in den meisten Fällen ein Protokoll verwenden:

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

Wenn SomeClass someMethod nicht implementiert, wird der folgende Fehler beim Kompilieren angezeigt:

error: type 'SomeClass' does not conform to protocol 'SomeProtocol'

29
Beachten Sie, dass dies nur für die oberste Klasse funktioniert, die das Protokoll implementiert. Alle Unterklassen können die Protokollanforderungen leichtfertig ignorieren.
Memmons

2
Auch die Verwendung von Generika für Protokolle wird nicht unterstützt = (
Dielson Sales

14

Eine andere Problemumgehung, wenn Sie nicht zu viele "virtuelle" Methoden haben, besteht darin, dass die Unterklasse die "Implementierungen" als Funktionsobjekte an den Basisklassenkonstruktor übergibt:

class MyVirtual {

    // 'Implementation' provided by subclass
    let fooImpl: (() -> String)

    // Delegates to 'implementation' provided by subclass
    func foo() -> String {
        return fooImpl()
    }

    init(fooImpl: (() -> String)) {
        self.fooImpl = fooImpl
    }
}

class MyImpl: MyVirtual {

    // 'Implementation' for super.foo()
    func myFoo() -> String {
        return "I am foo"
    }

    init() {
        // pass the 'implementation' to the superclass
        super.init(myFoo)
    }
}

1
Nicht so nützlich, wenn Sie noch ein paar virtuelle Methoden haben
Bushra Shahid

@ xs2bush Wenn mehr Ihrer Methoden virtuell sind als nicht, ist es wahrscheinlich besser, sie in einem Protokoll zu deklarieren und die nicht virtuellen Methoden über Erweiterungsmethoden bereitzustellen.
David Moles

1
Genau das habe ich getan
Bushra Shahid

0

Sie können das Protokoll gegen die Behauptung verwenden, wie in der Antwort hier von vorgeschlagen drewag. Ein Beispiel für das Protokoll fehlt jedoch. Ich decke hier ab,

Protokoll

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

Jetzt müssen alle Unterklassen das Protokoll implementieren, das in der Kompilierungszeit überprüft wird. Wenn SomeClass someMethod nicht implementiert, wird der folgende Fehler beim Kompilieren angezeigt:

Fehler: Typ 'SomeClass' entspricht nicht dem Protokoll 'SomeProtocol'

Hinweis: Dies funktioniert nur für die oberste Klasse, die das Protokoll implementiert. Alle Unterklassen können die Protokollanforderungen leichtfertig ignorieren. - wie kommentiert vonmemmons

Behauptung

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

Assertion funktioniert jedoch nur zur Laufzeit.


-2

Da ich neu in der iOS-Entwicklung bin, bin ich mir nicht ganz sicher, wann dies implementiert wurde. Eine Möglichkeit, das Beste aus beiden Welten herauszuholen, besteht darin, eine Erweiterung für ein Protokoll zu implementieren:

protocol ThingsToDo {
    func doThingOne()
}

extension ThingsToDo {
    func doThingTwo() { /* Define code here */}
}

class Person: ThingsToDo {
    func doThingOne() {
        // Already defined in extension
        doThingTwo()
        // Rest of code
    }
}

Mit der Erweiterung können Sie den Standardwert für eine Funktion festlegen, während die Funktion im regulären Protokoll weiterhin einen Fehler bei der Kompilierung liefert, wenn sie nicht definiert ist


abstrakte Funktionen sind das Gegenteil von Standardimplementierungen
Hogdotmac
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.