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?

JuJoDi
quelle
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

Drawag
quelle
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() { ... }

}
JMiguel
quelle
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'
Connor
quelle
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)
    }
}
David Moles
quelle
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.

Sazzad Hissain Khan
quelle
-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

Jordanien
quelle
abstrakte Funktionen sind das Gegenteil von Standardimplementierungen
Hogdotmac