Sintassi di cattura rapida

Cerco di capire la nuova cosa di gestione degli errori in swift 2. Ecco cosa ho fatto: ho prima dichiarato un errore enum:

enum SandwichError: ErrorType { case NotMe case DoItYourself } 

E poi ho dichiarato un metodo che genera un errore (non un’eccezione gente. È un errore). Ecco questo metodo:

 func makeMeSandwich(names: [String: String]) throws -> String { guard let sandwich = names["sandwich"] else { throw SandwichError.NotMe } return sandwich } 

Il problema è dal lato chiamante. Ecco il codice che chiama questo metodo:

 let kitchen = ["sandwich": "ready", "breakfeast": "not ready"] do { let sandwich = try makeMeSandwich(kitchen) print("i eat it \(sandwich)") } catch SandwichError.NotMe { print("Not me error") } catch SandwichError.DoItYourself { print("do it error") } 

Dopo che il compilatore della riga do dice che gli Errors thrown from here are not handled because the enclosing catch is not exhaustive . Ma secondo me è esaustivo perché ci sono solo due casi in SandwichError enum.

Per le normali dichiarazioni di switch, swift può capire che è esaustivo quando ogni caso è gestito.

Ci sono due punti importanti nel modello di gestione degli errori di Swift 2: esaustività e resilienza. Insieme, si riducono alla tua dichiarazione do / catch che ha bisogno di cogliere ogni ansible errore, non solo quelli che sai che puoi lanciare.

Si noti che non si dichiarano i tipi di errori che una funzione può generare, ma solo se essa genera. È un tipo di problema zero-uno-infinito: come qualcuno che definisce una funzione per gli altri (incluso il tuo sé futuro) da utilizzare, non vuoi dover rendere ogni cliente della tua funzione adatto ad ogni cambiamento nell’implementazione del tuo funzione, compresi gli errori che può generare. Vuoi un codice che chiami la tua funzione per essere resistente a tale cambiamento.

Poiché la tua funzione non è in grado di dire che tipo di errori genera (o potrebbe proiettare in futuro), i blocchi catch che lo catturano non sanno quali tipi di errori potrebbero generare. Quindi, oltre a gestire i tipi di errore che conosci, devi gestire quelli che non hai con una dichiarazione di catch universale – in questo modo se la tua funzione modifica l’insieme di errori che genera in futuro, i chiamanti continueranno a catturare i suoi errori.

 do { let sandwich = try makeMeSandwich(kitchen) print("i eat it \(sandwich)") } catch SandwichError.NotMe { print("Not me error") } catch SandwichError.DoItYourself { print("do it error") } catch let error { print(error.localizedDescription) } 

Ma non fermiamoci qui. Pensa ancora a questa idea di resilienza. Il modo in cui hai progettato il tuo sandwich, devi descrivere gli errori in ogni posto in cui li usi. Ciò significa che ogni volta che cambi il set di casi di errore, devi cambiare ogni luogo che li usa … non è molto divertente.

L’idea alla base della definizione dei tuoi tipi di errore è quella di permettere di centralizzare cose del genere. Puoi definire un metodo di description per i tuoi errori:

 extension SandwichError: CustomStringConvertible { var description: String { switch self { case NotMe: return "Not me error" case DoItYourself: return "Try sudo" } } } 

E poi il tuo codice di gestione degli errori può chiedere al tuo tipo di errore di descriversi – ora ogni posto dove gestisci gli errori può usare lo stesso codice e gestire anche possibili casi di errore futuri.

 do { let sandwich = try makeMeSandwich(kitchen) print("i eat it \(sandwich)") } catch let error as SandwichError { print(error.description) } catch { print("i dunno") } 

Questo apre anche la strada a tipi di errore (o estensioni su di essi) per supportare altri modi di riportare errori – ad esempio, potresti avere un’estensione sul tuo tipo di errore che sa come presentare un UIAlertController per riportare l’errore a un utente iOS .

Sospetto che non sia stato ancora implementato correttamente. La Swift Programming Guide sembra implicare che il compilatore possa dedurre corrispondenze esaustive come “un’istruzione switch”. Non fa menzione di aver bisogno di una catch generale per essere esauriente.

Noterai inoltre che l’errore si trova sulla linea di try , non alla fine del blocco, ovvero che a un certo punto il compilatore sarà in grado di individuare quale dichiarazione try nel blocco ha tipi di eccezioni non gestiti.

La documentazione è un po ‘ambigua però. Ho sfogliato il video ‘What’s new in Swift’ e non ho trovato alcun indizio; Continuerò a provare.

Aggiornare:

Ora siamo fino alla Beta 3 senza alcun accenno all’inferenza di ErrorType. Ora credo che se questo fosse mai stato pianificato (e penso ancora che fosse in qualche momento), il dispaccio dinamico sulle estensioni del protocollo probabilmente lo uccise.

Aggiornamento Beta 4:

Xcode 7b4 ha aggiunto il supporto per i commenti dei doc per Throws: :, che “dovrebbe essere usato per documentare quali errori possono essere generati e perché”. Immagino che questo almeno fornisca qualche meccanismo per comunicare gli errori ai consumatori delle API. Chi ha bisogno di un sistema di tipi quando si ha la documentazione!

Un altro aggiornamento:

Dopo aver trascorso un po ‘di tempo sperando in inferenze automatiche di ErrorType , e ErrorType capire quali sarebbero state le limitazioni di quel modello, ho cambiato idea: questo è ciò che spero che Apple implementa. Essenzialmente:

 // allow us to do this: func myFunction() throws -> Int // or this: func myFunction() throws CustomError -> Int // but not this: func myFunction() throws CustomErrorOne, CustomErrorTwo -> Int 

Ancora un altro aggiornamento

La logica di gestione degli errori di Apple è ora disponibile qui . Ci sono state anche alcune discussioni interessanti sulla mailing list di swift-evolution . Essenzialmente, John McCall si oppone agli errori di battitura perché crede che la maggior parte delle librerie finirà per includere comunque un errore generico e che gli errori tipografici non aggiungeranno molto al codice oltre al boilerplate (ha usato il termine ‘bluff aspirazionale’). Chris Lattner ha detto che è disponibile per errori tipografici in Swift 3 se può funzionare con il modello di resilienza.

Swift è preoccupato che la tua dichiarazione del caso non copra tutti i casi, per risolvere il problema è necessario creare un caso predefinito:

 do { let sandwich = try makeMeSandwich(kitchen) print("i eat it \(sandwich)") } catch SandwichError.NotMe { print("Not me error") } catch SandwichError.DoItYourself { print("do it error") } catch Default { print("Another Error") } 

Sono rimasto deluso anche dalla mancanza di un tipo che una funzione può lanciare, ma ora lo prendo grazie a @rickster e lo riassumerò in questo modo: diciamo che potremmo specificare il tipo che una funzione genera, avremmo qualcosa del genere:

 enum MyError: ErrorType { case ErrorA, ErrorB } func myFunctionThatThrows() throws MyError { ...throw .ErrorA...throw .ErrorB... } do { try myFunctionThatThrows() } case .ErrorA { ... } case .ErrorB { ... } 

Il problema è che anche se non cambiamo nulla in myFunctionThatThrows, se aggiungiamo un caso di errore a MyError:

 enum MyError: ErrorType { case ErrorA, ErrorB, ErrorC } 

siamo fregati perché il nostro do / try / catch non è più esaustivo, così come qualsiasi altro posto in cui abbiamo chiamato funzioni che lanciano MyError

Crea enum in questo modo:

 //Error Handling in swift enum spendingError : Error{ case minus case limit } 

Creare un metodo come:

  func calculateSpending(morningSpending:Double,eveningSpending:Double) throws ->Double{ if morningSpending < 0 || eveningSpending < 0{ throw spendingError.minus } if (morningSpending + eveningSpending) > 100{ throw spendingError.limit } return morningSpending + eveningSpending } 

Ora controlla l’errore o no e gestiscilo:

 do{ try calculateSpending(morningSpending: 60, eveningSpending: 50) } catch spendingError.minus{ print("This is not possible...") } catch spendingError.limit{ print("Limit reached...") }