Fehlerbehandlung in Swift-Sprache

190

Ich habe nicht zu viel in Swift gelesen, aber eines ist mir aufgefallen, dass es keine Ausnahmen gibt. Wie machen sie die Fehlerbehandlung in Swift? Hat jemand etwas im Zusammenhang mit der Fehlerbehandlung gefunden?

Peko
quelle
1
Ich fand Fehlermeldungen genau wie bei Obj-C: o
Arbitur
13
@Arbitur der gute alte Segfault Weg?
Peko
Erstellt einen NSTimer in Swift und als ich die Funktion falsch geschrieben habe, stürzte er ab und gab mir einen Fehler, dass er die Methode nicht finden konnte :)
Arbitur
3
Sie können die Unterstützung für Try-Catch für Swift hinzufügen, indem Sie den Anweisungen in diesem Artikel folgen
William Falcon
@peko Wie gehst du mit einem Segfault in Swift um? Ich denke nicht, dass es ab sofort möglich ist, was leider einige Fehler nicht behebbar macht
Orlin Georgiev

Antworten:

148

Swift 2 & 3

In Swift 2 haben sich die Dinge ein wenig geändert, da es einen neuen Fehlerbehandlungsmechanismus gibt, der Ausnahmen etwas ähnlicher ist, sich jedoch im Detail unterscheidet.

1. Anzeige der Fehlermöglichkeit

Wenn die Funktion / Methode angeben möchte, dass sie möglicherweise einen Fehler auslöst, sollte sie ein throwsSchlüsselwort wie dieses enthalten

func summonDefaultDragon() throws -> Dragon

Hinweis: Es gibt keine Spezifikation für die Art des Fehlers, den die Funktion tatsächlich auslösen kann. Diese Deklaration besagt lediglich, dass die Funktion eine Instanz eines beliebigen Typs auslösen kann, der ErrorType implementiert, oder überhaupt nicht auslöst.

2. Aufrufen einer Funktion, die Fehler auslösen kann

Um die Funktion aufzurufen, müssen Sie das Schlüsselwort try wie folgt verwenden

try summonDefaultDragon()

Diese Zeile sollte normalerweise wie folgt vorhanden sein

do {
    let dragon = try summonDefaultDragon() 
} catch DragonError.dragonIsMissing {
    // Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
    // Other specific-case error-handlng
} catch {
    // Catch all error-handling
}

Hinweis: Die catch-Klausel nutzt alle leistungsstarken Funktionen des Swift-Mustervergleichs, sodass Sie hier sehr flexibel sind.

Sie können den Fehler weitergeben, wenn Sie eine Wurffunktion von einer Funktion aus aufrufen, die selbst mit dem throwsSchlüsselwort markiert ist :

func fulfill(quest: Quest) throws {
    let dragon = try summonDefaultDragon()
    quest.ride(dragon)
} 

Alternativ können Sie die Wurffunktion aufrufen, indem Sie try?:

let dragonOrNil = try? summonDefaultDragon()

Auf diese Weise erhalten Sie entweder den Rückgabewert oder Null, wenn ein Fehler aufgetreten ist. Auf diese Weise erhalten Sie das Fehlerobjekt nicht.

Das bedeutet, dass Sie auch try?nützliche Aussagen wie:

if let dragon = try? summonDefaultDragon()

oder

guard let dragon = try? summonDefaultDragon() else { ... }

Schließlich können Sie entscheiden, dass Sie wissen, dass ein Fehler nicht tatsächlich auftritt (z. B. weil Sie bereits überprüft haben, ob die Voraussetzungen erfüllt sind), und das try!Schlüsselwort verwenden:

let dragon = try! summonDefaultDragon()

Wenn die Funktion tatsächlich einen Fehler auslöst, wird in Ihrer Anwendung ein Laufzeitfehler angezeigt, und die Anwendung wird beendet.

3. Einen Fehler auslösen

Um einen Fehler auszulösen, verwenden Sie das Schlüsselwort throw wie dieses

throw DragonError.dragonIsMissing

Sie können alles werfen, was dem ErrorTypeProtokoll entspricht . Für den Anfang NSErrorentspricht dies diesem Protokoll, aber Sie möchten wahrscheinlich enum-based verwenden ErrorType, mit dem Sie mehrere verwandte Fehler gruppieren können, möglicherweise mit zusätzlichen Daten wie diesen

enum DragonError: ErrorType {
    case dragonIsMissing
    case notEnoughMana(requiredMana: Int)
    ...
}

Die Hauptunterschiede zwischen dem neuen Swift 2 & 3-Fehlermechanismus und Ausnahmen im Java / C # / C ++ - Stil sind folgende:

  • Syntax ist ein wenig anders: do-catch+ try+ defervs traditionelle try-catch-finallySyntax.
  • Die Ausnahmebehandlung verursacht im Ausnahmepfad normalerweise eine viel höhere Ausführungszeit als im Erfolgspfad. Dies ist bei Swift 2.0-Fehlern nicht der Fall, bei denen Erfolgspfad und Fehlerpfad ungefähr gleich viel kosten.
  • Der gesamte Fehlerauslösecode muss deklariert werden, während Ausnahmen möglicherweise von irgendwoher ausgelöst wurden. Alle Fehler sind "geprüfte Ausnahmen" in der Java-Nomenklatur. Im Gegensatz zu Java geben Sie jedoch keine potenziell ausgelösten Fehler an.
  • Schnelle Ausnahmen sind nicht mit ObjC-Ausnahmen kompatibel. Ihr do-catchBlock fängt keine NSException ab und umgekehrt, dafür müssen Sie ObjC verwenden.
  • Schnelle Ausnahmen sind mit den NSErrorKonventionen der Cocoa- Methode kompatibel, bei denen entweder false(für die BoolRückgabe von Funktionen) oder nil(für die AnyObjectRückgabe von Funktionen) zurückgegeben und NSErrorPointerFehlerdetails übergeben werden.

Als zusätzlichen synthetischen Zucker zur Vereinfachung der Fehlerbehandlung gibt es zwei weitere Konzepte

  • Zurückgestellte Aktionen (mithilfe des deferSchlüsselworts), mit denen Sie den gleichen Effekt erzielen können wie beim endgültigen Blockieren in Java / C # / etc.
  • Guard-Anweisung (mit guardSchlüsselwort), mit der Sie etwas weniger if / else-Code schreiben können als im normalen Fehlerprüf- / Signalisierungscode.

Schnell 1

Laufzeitfehler:

Wie Leandros zur Behandlung von Laufzeitfehlern (wie Netzwerkverbindungsproblemen, Analysieren von Daten, Öffnen von Dateien usw.) NSErrorvorschlägt, sollten Sie diese wie in ObjC verwenden, da Foundation, AppKit, UIKit usw. ihre Fehler auf diese Weise melden. Es ist also mehr Rahmensache als Sprachsache.

Ein weiteres häufiges Muster, das verwendet wird, sind Separator-Erfolgs- / Fehlerblöcke wie in AFNetworking:

var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
    success: { (NSURLSessionDataTask) -> Void in
        println("Success")
    },
    failure:{ (NSURLSessionDataTask, NSError) -> Void in
        println("Failure")
    })

Trotzdem hat der Fehlerblock häufig eine NSErrorInstanz empfangen , die den Fehler beschreibt.

Programmiererfehler:

Für Programmiererfehler (z. B. Zugriff außerhalb des Bereichs auf Array-Elemente, ungültige Argumente, die an einen Funktionsaufruf übergeben wurden usw.) haben Sie Ausnahmen in ObjC verwendet. Swift Sprache scheint keine Sprachunterstützung für Ausnahmen zu haben (wie throw, catchetc Schlüsselwort). Wie aus der Dokumentation hervorgeht, läuft es jedoch zur gleichen Laufzeit wie ObjC, und daher können Sie immer noch Folgendes werfen NSExceptions:

NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()

Sie können sie einfach nicht in reinem Swift abfangen, obwohl Sie sich möglicherweise dafür entscheiden, Ausnahmen im ObjC-Code abzufangen.

Die Frage ist, ob Sie Ausnahmen für Programmiererfehler auslösen oder Behauptungen verwenden sollten, wie Apple im Sprachhandbuch vorschlägt.

MDJ
quelle
20
"Netzwerkverbindungsprobleme" und "Öffnen von Dateien" mithilfe der Cocoa-APIs (NSFileHandle) können Ausnahmen auslösen, die abgefangen werden müssen. Ohne Ausnahmen in Swift müssen Sie diesen Teil Ihres Programms in Objective-C implementieren oder Ihre gesamte Arbeit mit den BSD C-APIs ausführen (beides sind schlechte Workarounds). Weitere Informationen finden Sie in der Dokumentation zu NSFileHandle.writeData ... developer.apple.com/library/ios/documentation/Cocoa/Reference/… :
Matt Gallagher
5
Auch hier bedeutet keine Ausnahmebehandlung eine zweistufige Objektkonstruktion mit all ihren inhärenten Problemen. Siehe stroustrup.com/except.pdf .
Phil
2
das fatalError(...)ist auch das gleiche.
Holex
8
So sehr ich Swift auch mag, ich denke, dies ist eine katastrophale Entscheidung, und nachdem sie einige der Konsequenzen kennengelernt haben, spielen sie mit dieser Auslassung mit dem Feuer ...
Rob
2
Ja, geprüfte Ausnahmen werden jetzt sparsam verwendet, da festgestellt wurde, dass der Programmierer gezwungen ist, Ausnahmen abzufangen, und nur wenig Hoffnung hat, den Code für Umweltverschmutzungen auf eine Art und Weise wiederherzustellen, die das Prinzip der Einzelverantwortung verletzt. Eine Klasse auf Domänenebene möchte sich nicht mit Ausnahmen auf der Infrastrukturebene befassen müssen. Daher werden derzeit nicht geprüfte Ausnahmen bevorzugt, und die interessierte Partei kann sie gegebenenfalls abfangen. . Überprüft = definitiv wiederherstellbar. Deaktiviert = nicht / möglicherweise wiederherstellbar.
Jasper Blues
69

Update 9. Juni 2015 - Sehr wichtig

Swift 2.0 kommt mit try, throwund catchKeywords und die aufregendste ist:

Swift übersetzt Objective-C-Methoden, die Fehler erzeugen, automatisch in Methoden, die einen Fehler gemäß der nativen Fehlerbehandlungsfunktion von Swift auslösen.

Hinweis: Methoden, die Fehler verbrauchen, wie z. B. Delegierungsmethoden oder Methoden, die einen Vervollständigungshandler mit einem NSError-Objektargument verwenden, werden nicht zu Methoden, die beim Import von Swift ausgelöst werden.

Auszug aus: Apple Inc. "Verwenden von Swift mit Kakao und Objective-C (Swift 2 Prerelease)." iBooks.

Beispiel: (aus dem Buch)

NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error){
    NSLog(@"Error: %@", error.domain);
}

Das Äquivalent in schnell wird sein:

let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("path/to/file")
do {
    try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
    print ("Error: \(error.domain)")
}

Fehler auslösen:

*errorPtr = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo: nil]

Wird automatisch an den Anrufer weitergegeben:

throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)

Aus Apple-Büchern, The Swift Programming Language, geht hervor, dass Fehler mit enum behandelt werden sollten.

Hier ist ein Beispiel aus dem Buch.

enum ServerResponse {
    case Result(String, String)
    case Error(String)
}

let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")

switch success {
case let .Result(sunrise, sunset):
    let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
    let serverResponse = "Failure...  \(error)"
}

Von: Apple Inc. "Die schnelle Programmiersprache". iBooks. https://itun.es/br/jEUH0.l

Aktualisieren

Aus Apple-Nachrichtenbüchern "Verwenden von Swift mit Kakao und Objective-C". Laufzeitausnahmen treten nicht in schnellen Sprachen auf, daher haben Sie keinen Try-Catch. Stattdessen verwenden Sie die optionale Verkettung .

Hier ist eine Strecke aus dem Buch:

In der folgenden Codeliste werden beispielsweise die erste und die zweite Zeile nicht ausgeführt, da die Eigenschaft length und die Methode characterAtIndex: für ein NSDate-Objekt nicht vorhanden sind. Die myLength-Konstante wird als optionales Int abgeleitet und auf Null gesetzt. Sie können auch eine if-let-Anweisung verwenden, um das Ergebnis einer Methode, auf die das Objekt möglicherweise nicht reagiert, bedingt zu entpacken, wie in Zeile drei gezeigt

let myLength = myObject.length?
let myChar = myObject.characterAtIndex?(5)
if let fifthCharacter = myObject.characterAtIndex(5) {
    println("Found \(fifthCharacter) at index 5")
}

Auszug aus: Apple Inc. "Verwenden von Swift mit Kakao und Objective-C." iBooks. https://itun.es/br/1u3-0.l


In den Büchern wird außerdem empfohlen, das Kakaofehlermuster von Objective-C (NSError Object) zu verwenden.

Die Fehlerberichterstattung in Swift folgt dem gleichen Muster wie in Objective-C, mit dem zusätzlichen Vorteil, optionale Rückgabewerte anzubieten. Im einfachsten Fall geben Sie einen Bool-Wert von der Funktion zurück, um anzugeben, ob dies erfolgreich war oder nicht. Wenn Sie den Grund für den Fehler melden müssen, können Sie der Funktion einen NSError out-Parameter vom Typ NSErrorPointer hinzufügen. Dieser Typ entspricht in etwa dem NSError ** von Objective-C, bietet zusätzliche Speichersicherheit und optionale Eingabe. Mit dem Präfix & Operator können Sie einen Verweis auf einen optionalen NSError-Typ als NSErrorPointer-Objekt übergeben, wie in der folgenden Codeliste gezeigt.

var writeError : NSError?
let written = myString.writeToFile(path, atomically: false,
    encoding: NSUTF8StringEncoding,
    error: &writeError)
if !written {
    if let error = writeError {
        println("write failure: \(error.localizedDescription)")
    }
}

Auszug aus: Apple Inc. "Verwenden von Swift mit Kakao und Objective-C." iBooks. https://itun.es/br/1u3-0.l

Guilherme Torres Castro
quelle
Für die letzte Anweisung sollte es sein: do {try myString.writeToFile (Pfad, atomar: true, Codierung: NSUTF8StringEncoding)} catch let error als NSError {print (error)}
Jacky
1
@Jacky Ja, das gilt für Swift 2.0. Obwohl diese Antwort vor der Veröffentlichung von Swift 2.0 geschrieben wurde, habe ich die Antwort aktualisiert, um die neue Art und Weise zu zeigen, wie Fehler in Swift 2.0 behandelt werden. Ich habe darüber nachgedacht, diesen Weg als Referenz zuzulassen, aber ich werde erwägen, die gesamte Antwort zu aktualisieren, um nur Swift 2.0 zu verwenden
Guilherme Torres Castro
12

In Swift gibt es keine Ausnahmen, ähnlich wie bei Objective-C.

In der Entwicklung können Sie asserteventuell auftretende Fehler abfangen, die vor Produktionsbeginn behoben werden müssen.

Der klassische NSErrorAnsatz wird nicht geändert, Sie senden eine NSErrorPointer, die gefüllt wird.

Kurzes Beispiel:

var error: NSError?
var contents = NSFileManager.defaultManager().contentsOfDirectoryAtPath("/Users/leandros", error: &error)
if let error = error {
    println("An error occurred \(error)")
} else {
    println("Contents: \(contents)")
}
Leandros
quelle
6
Dies wirft zwei Fragen auf: Was passiert, wenn der von Swift aufgerufene ObjC-Code tatsächlich eine Ausnahme auslöst und ob NSError unser universelles Fehlerobjekt wie in ObjC ist?
MDJ
1
Ist es nur eine Tatsache im Leben von Swift, dass Initialisierer nicht scheitern oder nicht scheitern können?
Phil
11
Die Ausnahmebehandlung scheint ziemlich schmutzig
Tash Pemhiwa
27
Ja, wer braucht Ausnahmen, wenn man nur abstürzen kann? Oder setzen Sie einen NSError ** als Argument in alle von Ihnen deklarierten Funktionen? so dass jeder f();g();wird f(&err);if(err) return;g(&err);if(err) return;für den ersten Monat, dann ist es einfach gewordenf(nil);g(nil);hopeToGetHereAlive();
hariseldon78
2
Diese Antwort ist sowohl veraltet (Swift unterstützt jetzt Ausnahmen) als auch falsch (Objective-C unterstützt Ausnahmen.
Rog
11

Der empfohlene "Swift Way" ist:

func write(path: String)(#error: NSErrorPointer) -> Bool { // Useful to curry error parameter for retrying (see below)!
    return "Hello!".writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: error)
}

var writeError: NSError?
let written = write("~/Error1")(error: &writeError)
if !written {
    println("write failure 1: \(writeError!.localizedDescription)")
    // assert(false) // Terminate program
}

Ich bevorzuge jedoch try / catch, da ich es einfacher finde, zu folgen, da dadurch die Fehlerbehandlung am Ende in einen separaten Block verschoben wird. Diese Anordnung wird manchmal als "Goldener Pfad" bezeichnet. Zum Glück können Sie dies mit Verschlüssen tun:

TryBool {
    write("~/Error2")(error: $0) // The code to try
}.catch {
    println("write failure 2: \($0!.localizedDescription)") // Report failure
    // assert(false) // Terminate program
}

Es ist auch einfach, eine Wiederholungsfunktion hinzuzufügen:

TryBool {
    write("~/Error3")(error: $0) // The code to try
}.retry {
    println("write failure 3 on try \($1 + 1): \($0!.localizedDescription)")
    return write("~/Error3r")  // The code to retry
}.catch {
    println("write failure 3 catch: \($0!.localizedDescription)") // Report failure
    // assert(false) // Terminate program
}

Die Auflistung für TryBool lautet:

class TryBool {
    typealias Tryee = NSErrorPointer -> Bool
    typealias Catchee = NSError? -> ()
    typealias Retryee = (NSError?, UInt) -> Tryee

    private var tryee: Tryee
    private var retries: UInt = 0
    private var retryee: Retryee?

    init(tryee: Tryee) {
        self.tryee = tryee
    }

    func retry(retries: UInt, retryee: Retryee) -> Self {
        self.retries = retries
        self.retryee = retryee
        return self
    }
    func retry(retryee: Retryee) -> Self {
        return self.retry(1, retryee)
    }
    func retry(retries: UInt) -> Self {
        // For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
        self.retries = retries
        retryee = nil
        return self
    }
    func retry() -> Self {
        return retry(1)
    }

    func catch(catchee: Catchee) {
        var error: NSError?
        for numRetries in 0...retries { // First try is retry 0
            error = nil
            let result = tryee(&error)
            if result {
                return
            } else if numRetries != retries {
                if let r = retryee {
                    tryee = r(error, numRetries)
                }
            }
        }
        catchee(error)
    }
}

Sie können eine ähnliche Klasse zum Testen eines optionalen Rückgabewerts anstelle des Bool-Werts schreiben:

class TryOptional<T> {
    typealias Tryee = NSErrorPointer -> T?
    typealias Catchee = NSError? -> T
    typealias Retryee = (NSError?, UInt) -> Tryee

    private var tryee: Tryee
    private var retries: UInt = 0
    private var retryee: Retryee?

    init(tryee: Tryee) {
        self.tryee = tryee
    }

    func retry(retries: UInt, retryee: Retryee) -> Self {
        self.retries = retries
        self.retryee = retryee
        return self
    }
    func retry(retryee: Retryee) -> Self {
        return retry(1, retryee)
    }
    func retry(retries: UInt) -> Self {
        // For some reason you can't write the body as "return retry(1, nil)", the compiler doesn't like the nil
        self.retries = retries
        retryee = nil
        return self
    }
    func retry() -> Self {
        return retry(1)
    }

    func catch(catchee: Catchee) -> T {
        var error: NSError?
        for numRetries in 0...retries {
            error = nil
            let result = tryee(&error)
            if let r = result {
                return r
            } else if numRetries != retries {
                if let r = retryee {
                    tryee = r(error, numRetries)
                }
            }
        }
        return catchee(error)
    }
}

Die TryOptional-Version erzwingt einen nicht optionalen Rückgabetyp, der die nachfolgende Programmierung erleichtert, z. B. 'Swift Way:

struct FailableInitializer {
    init?(_ id: Int, error: NSErrorPointer) {
        // Always fails in example
        if error != nil {
            error.memory = NSError(domain: "", code: id, userInfo: [:])
        }
        return nil
    }
    private init() {
        // Empty in example
    }
    static let fallback = FailableInitializer()
}

func failableInitializer(id: Int)(#error: NSErrorPointer) -> FailableInitializer? { // Curry for retry
    return FailableInitializer(id, error: error)
}

var failError: NSError?
var failure1Temp = failableInitializer(1)(error: &failError)
if failure1Temp == nil {
    println("failableInitializer failure code: \(failError!.code)")
    failure1Temp = FailableInitializer.fallback
}
let failure1 = failure1Temp! // Unwrap

Verwenden von TryOptional:

let failure2 = TryOptional {
    failableInitializer(2)(error: $0)
}.catch {
    println("failableInitializer failure code: \($0!.code)")
    return FailableInitializer.fallback
}

let failure3 = TryOptional {
    failableInitializer(3)(error: $0)
}.retry {
    println("failableInitializer failure, on try \($1 + 1), code: \($0!.code)")
    return failableInitializer(31)
}.catch {
    println("failableInitializer failure code: \($0!.code)")
    return FailableInitializer.fallback
}

Beachten Sie das automatische Auspacken.

Howard Lovatt
quelle
7

Bearbeiten: Obwohl diese Antwort funktioniert, ist sie kaum mehr als Objective-C, das in Swift transkribiert wurde. Es wurde durch Änderungen in Swift 2.0 überholt. Die Antwort von Guilherme Torres Castro oben ist eine sehr gute Einführung in die bevorzugte Art der Fehlerbehandlung in Swift. VOS

Es hat ein bisschen gedauert, es herauszufinden, aber ich glaube, ich habe es verstanden. Es scheint aber hässlich. Nichts weiter als eine dünne Haut über der Objective-C-Version.

Aufruf einer Funktion mit einem NSError-Parameter ...

var fooError : NSError ? = nil

let someObject = foo(aParam, error:&fooError)

// Check something was returned and look for an error if it wasn't.
if !someObject {
   if let error = fooError {
      // Handle error
      NSLog("This happened: \(error.localizedDescription)")
   }
} else {
   // Handle success
}`

Schreiben der Funktion, die einen Fehlerparameter akzeptiert ...

func foo(param:ParamObject, error: NSErrorPointer) -> SomeObject {

   // Do stuff...

   if somethingBadHasHappened {
      if error {
         error.memory = NSError(domain: domain, code: code, userInfo: [:])
      }
      return nil
   }

   // Do more stuff...
}
Vince O'Sullivan
quelle
5

Grundlegender Wrapper um Ziel C, der Ihnen die Try-Catch-Funktion bietet. https://github.com/williamFalcon/SwiftTryCatch

Verwenden Sie wie:

SwiftTryCatch.try({ () -> Void in
        //try something
     }, catch: { (error) -> Void in
        //handle error
     }, finally: { () -> Void in
        //close resources
})
William Falcon
quelle
Gute Idee. Wer sich jedoch dafür entscheidet, muss berücksichtigen, dass im try-Block zugewiesene Objekte nicht freigegeben werden, wenn eine Ausnahme ausgelöst wird. Dies kann Probleme mit Zombie-Objekten verursachen und jede Verwendung von RAII wird beeinträchtigt (automatische Entsperrung, automatische SQL-Festschreibung, automatische SQL-Rollback ...). Vielleicht könnte uns c ++ bei irgendeiner Form von "runAtExit" helfen?
Hariseldon78
Update: Ich habe gerade festgestellt, dass es ein Flag in Clang gibt, um die Freigabe von Objekten beim Auslösen von Ausnahmen zu ermöglichen: -fobjc-arc-Ausnahmen. Ich muss versuchen, ob es noch mit der verpackten Version funktioniert (ich denke, es sollte)
hariseldon78
Wenn Sie diese Option verwenden, beachten Sie, dass die Codegröße zunimmt, da der Compiler semi-ausnahmesicheren Code generieren muss. Außerdem: Es ist möglicherweise nicht die beste Idee, sich auf eine solche Compilerfunktion zu verlassen. Ausnahmen gelten nur für Programmiererfehler. Daher lohnt es sich nicht, diese Compiler-Option zu verwenden, um während der Entwicklung ein wenig Speicherplatz zu sparen. Wenn Ihr Produktionscode Ausnahmen enthält, sollten Sie sich zunächst mit dem Problem befassen, das diese Ausnahmen verursacht.
Christian Kienle
1
Es kann Situationen geben, die außerhalb Ihrer Kontrolle liegen. Zum Beispiel das Parsen von json im falschen Format.
William Falcon
3

Dies ist eine Update-Antwort für Swift 2.0. Ich freue mich auf ein funktionsreiches Fehlerbehandlungsmodell wie in Java. Schließlich kündigten sie die guten Nachrichten an. Hier

Fehlerbehandlungsmodell: Das neue Fehlerbehandlungsmodell in Swift 2.0 fühlt sich sofort natürlich an, mit bekannten Schlüsselwörtern zum Ausprobieren, Werfen und Abfangen . Das Beste ist, dass es perfekt mit den Apple SDKs und NSError zusammenarbeitet. Tatsächlich entspricht NSError dem ErrorType eines Swift. Sie werden auf jeden Fall die WWDC-Sitzung über Neuigkeiten in Swift sehen wollen, um mehr darüber zu erfahren.

z.B :

func loadData() throws { }
func test() {
do {
    try loadData()
} catch {
    print(error)
}}
Paraneetharan Saravanaperumal
quelle
3

Als Guilherme Torres Castro sagte in Swift 2.0, try, catch, dokann bei der Programmierung verwendet werden.

Zum Beispiel in Coredata Daten Methode holen, statt put &errorals Parameter in die managedContext.executeFetchRequest(fetchRequest, error: &error)jetzt nur Gebrauch verwenden müssen wir managedContext.executeFetchRequest(fetchRequest)dann den Fehler behandeln mit try, catch( Apple - Document Link )

do {
   let fetchedResults = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
   if let results = fetchedResults{
      people = results
   }
} catch {
   print("Could not fetch")
}

Wenn Sie den xcode7 Beta bereits heruntergeladen haben. Versuchen Sie, Fehler in der Dokumentation und in der API-Referenz zu suchen , und wählen Sie das erste angezeigte Ergebnis aus. Es gibt eine grundlegende Vorstellung davon, was für diese neue Syntax getan werden kann. Für viele APIs ist jedoch noch keine vollständige Dokumentation verfügbar.

Weitere ausgefallene Techniken zur Fehlerbehandlung finden Sie in

Was ist neu in Swift (2015 Session 106 28m30s)

Zingoer
quelle
1

Schöne und einfache Bibliothek , um Ausnahmen zu behandeln: TryCatchFinally-Swift

Wie einige andere werden auch die objektiven C-Ausnahmefunktionen behandelt.

Verwenden Sie es so:

try {
    println("  try")
}.catch { e in
    println("  catch")
}.finally {
    println("  finally")
}
Morten Holmgaard
quelle
Ich habe ein Beispiel hinzugefügt :)
Morten Holmgaard
Es ist wahrscheinlich erwähnenswert, die Meinung des Autors zu erwähnen: "Warnung: Dies ist ein Hack für Spaß und Böses. Widerstehen Sie der Versuchung, ihn zu benutzen."
jbat100
1

Beginnend mit Swift 2 wird die Fehlerbehandlung, wie bereits erwähnt, am besten durch die Verwendung der Aufzählungen do / try / catch und ErrorType erreicht. Dies funktioniert für synchrone Methoden recht gut, für die asynchrone Fehlerbehandlung ist jedoch ein wenig Cleverness erforderlich.

Dieser Artikel hat einen großartigen Ansatz für dieses Problem:

https://jeremywsherman.com/blog/2015/06/17/using-swift-throws-with-completion-callbacks/

Zusammenfassen:

// create a typealias used in completion blocks, for cleaner code
typealias LoadDataResult = () throws -> NSData

// notice the reference to the typealias in the completionHandler
func loadData(someID: String, completionHandler: LoadDataResult -> Void)
    {
    completionHandler()
    }

dann wäre der Aufruf der obigen Methode wie folgt:

self.loadData("someString",
    completionHandler:     
        { result: LoadDataResult in
        do
            {
            let data = try result()
            // success - go ahead and work with the data
            }
        catch
            {
            // failure - look at the error code and handle accordingly
            }
        })

Dies scheint etwas sauberer zu sein, als wenn ein separater errorHandler-Rückruf an die asynchrone Funktion übergeben wird. So würde dies vor Swift 2 gehandhabt.

Gene Loparco
quelle
0

Was ich gesehen habe, ist, dass Sie aufgrund der Art des Geräts keine Reihe von kryptischen Fehlerbehandlungsnachrichten auf den Benutzer werfen möchten. Aus diesem Grund geben die meisten Funktionen optionale Werte zurück, und Sie codieren nur, um die optionalen Werte zu ignorieren. Wenn eine Funktion auf Null zurückkommt, was bedeutet, dass sie fehlgeschlagen ist, können Sie eine Nachricht oder was auch immer einfügen.

Cheborneck
quelle
1
Wenn Sie eine Null zurückgeben, werden keine Informationen zur Art des Fehlers zurückgegeben. Wenn ein Fehlerobjekt zurückgegeben wird, wenn ein Fehler auftritt, kann der Programmierer abhängig vom Fehler wählen, ob er es ignorieren, behandeln, in die Luft sprudeln lassen oder "eine Nachricht oder was auch immer einfügen" möchte. Wissen ist Macht.
Vince O'Sullivan