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?
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?
Antworten:
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.
Wenn die Funktion / Methode angeben möchte, dass sie möglicherweise einen Fehler auslöst, sollte sie ein throws
Schlü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.
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 throws
Schlü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.
Um einen Fehler auszulösen, verwenden Sie das Schlüsselwort throw wie dieses
throw DragonError.dragonIsMissing
Sie können alles werfen, was dem ErrorType
Protokoll entspricht . Für den Anfang NSError
entspricht 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:
do-catch
+ try
+ defer
vs traditionelle try-catch-finally
Syntax.do-catch
Block fängt keine NSException ab und umgekehrt, dafür müssen Sie ObjC verwenden.NSError
Konventionen der Cocoa- Methode kompatibel, bei denen entweder false
(für die Bool
Rückgabe von Funktionen) oder nil
(für die AnyObject
Rückgabe von Funktionen) zurückgegeben und NSErrorPointer
Fehlerdetails übergeben werden.Als zusätzlichen synthetischen Zucker zur Vereinfachung der Fehlerbehandlung gibt es zwei weitere Konzepte
defer
Schlüsselworts), mit denen Sie den gleichen Effekt erzielen können wie beim endgültigen Blockieren in Java / C # / etc.guard
Schlüsselwort), mit der Sie etwas weniger if / else-Code schreiben können als im normalen Fehlerprüf- / Signalisierungscode.Laufzeitfehler:
Wie Leandros zur Behandlung von Laufzeitfehlern (wie Netzwerkverbindungsproblemen, Analysieren von Daten, Öffnen von Dateien usw.) NSError
vorschlä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 NSError
Instanz 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
, catch
etc 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.
fatalError(...)
ist auch das gleiche.
Update 9. Juni 2015 - Sehr wichtig
Swift 2.0 kommt mit try
, throw
und catch
Keywords 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
In Swift gibt es keine Ausnahmen, ähnlich wie bei Objective-C.
In der Entwicklung können Sie assert
eventuell auftretende Fehler abfangen, die vor Produktionsbeginn behoben werden müssen.
Der klassische NSError
Ansatz 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)")
}
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();
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.
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...
}
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
})
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)
}}
Als Guilherme Torres Castro sagte in Swift 2.0, try
, catch
, do
kann bei der Programmierung verwendet werden.
Zum Beispiel in Coredata Daten Methode holen, statt put &error
als 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)
Die Fehlerbehandlung ist eine neue Funktion von Swift 2.0. Es nutzt die try
, throw
und catch
Schlüsselwörter.
Siehe die Ankündigung von Apple Swift 2.0 im offiziellen Apple Swift-Blog
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")
}
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.
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.