2015-01-07 7 views
9

Da Swift keine abstrakten Methoden hat, erstelle ich eine Methode, deren Standardimplementierung bedingungslos einen Fehler auslöst. Dies erzwingt, dass eine Unterklasse die abstrakte Methode überschreibt. Mein Code sieht wie folgt aus:Swift überzeugend, dass eine Funktion aufgrund einer geworfenen Exception nie zurückgegeben wird

class SuperClass { 
    func shouldBeOverridden() -> ReturnType { 
     let exception = NSException(
      name: "Not implemented!", 
      reason: "A concrete subclass did not provide its own implementation of shouldBeOverridden()", 
      userInfo: nil 
     ) 
     exception.raise() 
    } 
} 

Das Problem: Da die Funktion einen Wert zurückgeben soll, und die obige Funktion hat keine return Anweisung, nicht Kompilierung. Ich brauche einen Weg, um den Compiler davon zu überzeugen, dass diese Funktion die Ausführung niemals beenden kann, weil immer ein Fehler auftritt.

Wie könnte dies in Swift getan werden, wenn die gesamte Fehlerbehandlung auf Bibliotheksebene und daher jenseits des Verständnisses des Compilers zu sein scheint? Gibt es irgendeine Funktion auf Sprachenebene, um (hoffentlich elegant) die Ausführung eines Programms zu beenden?

+0

Während Sie Ihre Frage mit einer Lösung selbst beantwortet haben, versuchen Sie ein schlechtes/falsches Problem zu lösen. Es gibt einen Grund, warum Swift keine abstrakten Methoden eingebaut hat. – nhgrif

Antwort

20

Swifts @noreturn Attribut markiert Funktionen und Methoden als nicht zu ihrem Aufrufer zurückzukehren.

Wie wahrscheinlich das einfachste Beispiel, die Signatur der integrierten Funktion abort() ‚s ist:

@noreturn func abort() 

Dieser dem Compiler alle Informationen gibt es braucht. Zum Beispiel wird kompiliert folgende ganz gut:

func alwaysFail() -> Int { 
    abort() 
} 

Obwohl alwaysFail() theoretisch gibt eine Int, weiß Swift, dass die Ausführung kann nicht fortgesetzt werden, nachdem abort() genannt wird.

Der Grund, dass mein ursprünglicher Code nicht funktionierte, ist, weil NSException.raise eine vor-Swift-Methode ist und daher nicht über das @noreturn-Attribut verfügt. Um dies leicht zu lösen, kann ich entweder verwenden abort():

func shouldBeOverridden() -> ReturnType { 
    println("Subclass has not implemented abstract method `shouldBeOverridden`!") 
    abort() 
} 

oder, wenn ich noch verwenden mag NSException, kann ich eine Erweiterung mit dem richtigen Attribute definiert

extension NSException { 
    @noreturn func noReturnRaise() { 
     self.raise() 
     abort() // This will never run, but Swift will complain that the function isn't really @noreturn if I don't call a @noreturn function before execution finishes. 
    } 
} 

Als dritte Option, I kann einfach einen nie aufgerufenen abort() nach einem NSException.raise() verwenden, um den Compiler zu beschwichtigen. Die frühere Option, mit einem extension, ist eigentlich nur eine Abstraktion, dies zu tun:

func shouldBeOverridden() -> ReturnType { 
    let exception = NSException(
     name: "Not implemented!", 
     reason: "A concrete subclass did not provide its own implementation of shouldBeOverridden()", 
     userInfo: nil 
    ) 
    exception.raise() 
    abort() // never called 
} 
2

Es hört sich an, als ob das, was Sie tun, besser erledigt wäre, indem Sie ein Protokoll erstellen und shouldBeOverridden eine erforderliche Methode machen, dann müssen Ihre Klassen diesem Protokoll entsprechen. https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html

+0

Dies könnte funktionieren, aber es gibt andere Elemente aller Unterklassen von 'SuperClass', die am besten mit einer gemeinsamen Implementierung in 'SuperClass' implementiert werden.Insbesondere gibt es mehrere gespeicherte Eigenschaften, die alle meine "Unterklassen" (oder Klassen, die dem Protokoll gemäß Ihrem Vorschlag entsprechen) haben müssen, und es wäre gut, diese nicht für jede einzelne Unterklasse neu deklarieren zu müssen. – SelectricSimian

+2

Ich würde empfehlen, die beiden Ansätze zu kombinieren und ein Protokoll sowie eine Basisklasse zu haben. Abstrakte Methoden sind in Objective-C oder Swift nicht sehr häufig, und ich habe keinen Fall gefunden, für den Protokolle keine bessere Wahl wären. Apple verwendet diesen Kombinationsansatz auch gelegentlich (siehe UIBarPositioning). –

+1

Ok, das funktioniert gut genug. Es scheint ein bisschen mehr wortreich, aber es macht Sinn und es ist ziemlich sauber. – SelectricSimian

8

In Xcode 8 Beta 6 (Swift 3 Beta 6) Sie können nun den Never Rückgabetyp statt @noreturn verwenden, um anzuzeigen, dass ein Die Funktion kehrt nicht zum Aufrufer zurück:

func crash() -> Never { 
    fatalError("Oops") 
}