Swift 2: Call kann werfen, ist aber nicht mit 'try' markiert und der Fehler wird nicht behandelt

161

Nachdem ich Xcode 7 Beta installiert und meinen Swift-Code in Swift 2 konvertiert habe, habe ich ein Problem mit dem Code, das ich nicht herausfinden kann. Ich weiß, dass Swift 2 neu ist, also suche ich und finde heraus, da es nichts gibt, sollte ich eine Frage schreiben.

Hier ist der Fehler:

Anruf kann werfen, ist aber nicht mit 'try' markiert und der Fehler wird nicht behandelt

Code:

func deleteAccountDetail(){
        let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
        let request = NSFetchRequest()
        request.entity = entityDescription

        //The Line Below is where i expect the error
        let fetchedEntities = self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
        }

        do {
            try self.Context!.save()
        } catch _ {
        }

    }

Schnappschuss: Geben Sie hier die Bildbeschreibung ein

Farhad
quelle

Antworten:

168

Sie müssen den Fehler abfangen, genau wie Sie es bereits für Ihren save()Anruf getan haben. Da Sie hier mehrere Fehler behandeln, können Sie trymehrere Anrufe nacheinander in einem einzigen Do-Catch-Block ausführen, wie folgt:

func deleteAccountDetail() {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()
    request.entity = entityDescription

    do {
        let fetchedEntities = try self.Context!.executeFetchRequest(request) as! [AccountDetail]

        for entity in fetchedEntities {
            self.Context!.deleteObject(entity)
        }

        try self.Context!.save()
    } catch {
        print(error)
    }
}

Oder wie @ bames53 in den Kommentaren unten hervorhob, ist es oft besser, den Fehler nicht dort abzufangen, wo er ausgelöst wurde. Sie können die Methode als throwsdann markieren, um die Methode tryaufzurufen. Beispielsweise:

func deleteAccountDetail() throws {
    let entityDescription = NSEntityDescription.entityForName("AccountDetail", inManagedObjectContext: Context!)
    let request = NSFetchRequest()

    request.entity = entityDescription

    let fetchedEntities = try Context.executeFetchRequest(request) as! [AccountDetail]

    for entity in fetchedEntities {
        self.Context!.deleteObject(entity)
    }

    try self.Context!.save()
}
Mick MacCallum
quelle
Dies hilft mir, es herauszufinden. Danke.
Farhad
5
Eigentlich ist es nicht erforderlich, dass die Ausnahme hier abgefangen wird. Es ist möglich, trydem Funktionsaufruf einfach das Schlüsselwort hinzuzufügen und diese Funktion als zu deklarieren func deleteAccountDetail() throw. Oder wenn Sie garantiert haben, dass die Funktion für die angegebene Eingabe nicht ausgelöst wird, können Sie verwenden try!.
Bames53
4
Ich erwähne dies nicht zu nitpick, aber weil es eigentlich ziemlich wichtig ist, eine anständige ausnahmebasierte Fehlerbehandlung durchzuführen, fangen die meisten Orte, an denen Ausnahmen auftreten, keine Ausnahmen. Es gibt drei Arten von Orten, an denen Ausnahmen angebracht sind. An allen anderen Stellen sollte Code Ausnahmen nicht explizit behandeln und sich auf implizite deinit()Aufrufe stützen , um eine Bereinigung durchzuführen (dh RAII), oder gelegentlich defereine Ad-hoc-Bereinigung durchführen. Weitere
Informationen finden
Aber wie würden Sie die Funktion ausführen? Wenn ich mit @ bames53 weg gehe?
Farhad
1
@NickMoore Was die Swift-Entwickler sie nennen, macht keinen Unterschied darin, was sie tatsächlich sind. Das neue Fehlerbehandlungssystem von Swift ist eine Implementierung von Ausnahmen, da dieser Begriff im Rest der Branche häufig verwendet wird.
Bames53
41

Wenn Sie eine Funktion aufrufen, mit der throwsin Swift deklariert wurde, müssen Sie die Funktionsaufrufsite mit tryoder mit Anmerkungen versehen try!. Zum Beispiel mit einer Wurffunktion:

func willOnlyThrowIfTrue(value: Bool) throws {
  if value { throw someError }
}

Diese Funktion kann wie folgt aufgerufen werden:

func foo(value: Bool) throws {
  try willOnlyThrowIfTrue(value)
}

Hier kommentieren wir den Aufruf mit try, wodurch der Leser darauf hingewiesen wird, dass diese Funktion möglicherweise eine Ausnahme auslöst und die folgenden Codezeilen möglicherweise nicht ausgeführt werden. Wir haben diese Funktion auch mit zu annotieren throws, da diese Funktion eine Ausnahme auslösen könnte (dh, wenn willOnlyThrowIfTrue()wirft, dann foowird die Ausnahme nach oben automatisch erneut auslösen.

Wenn Sie eine Funktion aufrufen möchten, die als möglicherweise auslösend deklariert ist, von der Sie jedoch wissen, dass sie in Ihrem Fall nicht ausgelöst wird, weil Sie sie korrekt eingeben, können Sie sie verwenden try!.

func bar() {
  try! willOnlyThrowIfTrue(false)
}

Auf diese Weise müssen Sie, wenn Sie sicherstellen, dass der Code nicht ausgelöst wird, keinen zusätzlichen Code für das Boilerplate eingeben, um die Weitergabe von Ausnahmen zu deaktivieren.

try!wird zur Laufzeit erzwungen: Wenn Sie verwenden try!und die Funktion am Ende ausgelöst wird, wird die Ausführung Ihres Programms mit einem Laufzeitfehler beendet.

Der meiste Code für die Ausnahmebehandlung sollte wie folgt aussehen: Entweder Sie verbreiten Ausnahmen einfach nach oben, wenn sie auftreten, oder Sie richten Bedingungen so ein, dass ansonsten mögliche Ausnahmen ausgeschlossen sind. Jede Bereinigung anderer Ressourcen in Ihrem Code sollte durch Objektzerstörung (dh deinit()) oder manchmal durch defered-Code erfolgen.

func baz(value: Bool) throws {

  var filePath = NSBundle.mainBundle().pathForResource("theFile", ofType:"txt")
  var data = NSData(contentsOfFile:filePath)

  try willOnlyThrowIfTrue(value)

  // data and filePath automatically cleaned up, even when an exception occurs.
}

Wenn Sie aus irgendeinem Grund Bereinigungscode haben, der ausgeführt werden muss, aber nicht in einer deinit()Funktion enthalten ist, können Sie ihn verwenden defer.

func qux(value: Bool) throws {
  defer {
    print("this code runs when the function exits, even when it exits by an exception")
  }

  try willOnlyThrowIfTrue(value)
}

Der meiste Code, der sich mit Ausnahmen befasst, lässt sie einfach nach oben an Anrufer weitergeben und bereinigt auf dem Weg über deinit()oder defer. Dies liegt daran, dass die meisten Codes nicht wissen, wie sie mit Fehlern umgehen sollen. Es weiß, was schief gelaufen ist, aber es gibt nicht genügend Informationen darüber, was ein Code höherer Ebene versucht, um zu wissen, was gegen den Fehler zu tun ist. Es weiß nicht, ob es angemessen ist, dem Benutzer einen Dialog zu präsentieren, oder ob es erneut versucht werden sollte oder ob etwas anderes angemessen ist.

Code auf höherer Ebene sollte jedoch genau wissen, was im Fehlerfall zu tun ist. Ausnahmen ermöglichen es also, dass bestimmte Fehler von dem Ort, an dem sie anfänglich auftreten, zu dem Ort aufsteigen, an dem sie behandelt werden können.

Die Behandlung von Ausnahmen erfolgt über catchAnweisungen.

func quux(value: Bool) {
  do {
    try willOnlyThrowIfTrue(value)
  } catch {
    // handle error
  }
}

Sie können mehrere catch-Anweisungen haben, von denen jede eine andere Art von Ausnahme abfängt.

  do {
    try someFunctionThatThowsDifferentExceptions()
  } catch MyErrorType.errorA {
    // handle errorA
  } catch MyErrorType.errorB {
    // handle errorB
  } catch {
    // handle other errors
  }

Weitere Informationen zu Best Practices mit Ausnahmen finden Sie unter http://exceptionsafecode.com/ . Es richtet sich speziell an C ++, aber nach Prüfung des Swift-Ausnahmemodells gelten die Grundlagen meines Erachtens auch für Swift.

Einzelheiten zur Swift-Syntax und zum Fehlerbehandlungsmodell finden Sie im Buch The Swift Programming Language (Swift 2 Prerelease) .

bames53
quelle
Grundsätzlich kann sich catch selbst mit dem Fehler befassen? oder Eingabefunktion
Farhad
1
@BrianS Ich bin mir nicht sicher, was Sie genau fragen, insbesondere im Hinblick auf eine 'Eingabefunktion', aber 'catch' ist im Wesentlichen ein Synonym für 'handle' im Kontext von Ausnahmen. Das heißt, eine Ausnahme abzufangen und eine Ausnahme zu behandeln, ist für Programmiersprachen dasselbe.
Bames53
Ich habe einen Fehler, den ich nicht verstehe. Kannst du mir bitte helfen? Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
Farhad
@BrianS Es hört sich so an, als würden Sie irgendwo eine Funktion mit einer falschen Signatur verwenden. Etwas erwartet eine Funktion, die NSData?, NSURLResponse?, NSError?als Argumente verwendet wird, aber Sie geben ihr eine Funktion, die keine Argumente akzeptiert.
Bames53
Oder etwas erwartet, dass eine Funktion, die nicht deklariert ist, Ausnahmen auslöst, und Sie geben ihr eine Funktion, die Ausnahmen auslöst.
Bames53