Swift 2: la chiamata può essere lanciata, ma non è contrassegnata con "try" e l'errore non viene gestito


161

Dopo aver installato Xcode 7 beta e convertito il mio codice swift in Swift 2, ho avuto qualche problema con il codice che non riesco a capire. So che Swift 2 è nuovo, quindi cerco e capisco dato che non c'è nulla al riguardo, dovrei scrivere una domanda.

Ecco l'errore:

La chiamata può essere lanciata, ma non è contrassegnata con "provare" e l'errore non viene gestito

Codice:

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 _ {
        }

    }

Istantanea: inserisci qui la descrizione dell'immagine

Risposte:


168

Devi catturare l'errore proprio come stai già facendo per la tua save()chiamata e poiché stai gestendo più errori qui, puoi trypiù chiamate in sequenza in un singolo blocco do-catch, in questo modo:

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)
    }
}

O come ha sottolineato @ bames53 nei commenti qui sotto, spesso è meglio non catturare l'errore nel punto in cui è stato lanciato. È possibile contrassegnare il metodo come throwsallora tryper chiamare il metodo. Per esempio:

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()
}

Questo mi aiuta a capirlo, grazie.
Farhad,

5
In realtà non è necessario che l'eccezione venga rilevata qui. È possibile semplicemente aggiungere la tryparola chiave alla chiamata della funzione e dichiarare questa funzione come func deleteAccountDetail() throw. O se hai garantito che la funzione non verrà lanciata per l'input dato, puoi usare try!.
bames53

4
Non lo dico a nitpick, ma poiché in realtà è abbastanza importante per la gestione degli errori basata sulle eccezioni decente che la maggior parte dei luoghi in cui si verificano le eccezioni non rileva eccezioni. Esistono tre tipi di luoghi in cui è opportuno rilevare eccezioni. In tutti gli altri posti, il codice non dovrebbe gestire esplicitamente le eccezioni e dovrebbe fare affidamento su deinit()chiamate implicite per eseguire la pulizia (ovvero RAII) o utilizzare occasionalmente deferper eseguire una pulizia ad hoc. Vedi exceptionsafecode.com per ulteriori informazioni (parla di C ++, ma i principi di base si applicano anche alle eccezioni Swift.)
bames53

Ma come eseguiresti la funzione? Se vado con @ bames53 in modo?
Farhad,

1
@NickMoore Quello che gli sviluppatori Swift scelgono di chiamarli non fa differenza in quello che sono realmente. Il nuovo sistema di gestione degli errori di Swift è un'implementazione di eccezioni in quanto tale termine è comunemente usato in tutto il resto del settore.
bames53,

41

Quando si chiama una funzione dichiarata con throwsin Swift, è necessario annotare il sito di chiamata della funzione con tryo try!. Ad esempio, data una funzione di lancio:

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

questa funzione può essere chiamata come:

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

Qui annotiamo la chiamata con try, che chiama al lettore che questa funzione può generare un'eccezione e che eventuali righe di codice seguenti potrebbero non essere eseguite. Dobbiamo anche annotare questa funzione con throws, perché questa funzione potrebbe generare un'eccezione (ad esempio, quando viene willOnlyThrowIfTrue()generata, quindifoo viene generata ridiscuterà automaticamente l'eccezione verso l'alto.

Se vuoi chiamare una funzione che è dichiarata come possibilmente lanciata, ma che sai che non verrà lanciata nel tuo caso perché le stai dando l'input corretto, puoi usare try!.

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

In questo modo, quando si garantisce che il codice non verrà emesso, non è necessario inserire un codice extra sulla piastra di cottura per disabilitare la propagazione delle eccezioni.

try! viene applicato in fase di esecuzione: se si utilizza try! e la funzione termina il lancio, l'esecuzione del programma verrà chiusa con un errore di runtime.

La maggior parte del codice di gestione delle eccezioni dovrebbe apparire come sopra: o semplicemente si propagano le eccezioni verso l'alto quando si verificano o si impostano condizioni tali da escludere eventuali eccezioni. Qualsiasi ripulitura di altre risorse nel codice dovrebbe avvenire tramite la distruzione di oggetti (ad esempio deinit()) o talvolta tramite defercodice ed.

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.
}

Se per qualsiasi motivo hai il codice di pulizia che deve essere eseguito ma non è in una deinit()funzione, puoi usare 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)
}

La maggior parte del codice che si occupa delle eccezioni li ha semplicemente propagati verso l'alto ai chiamanti, facendo pulizia sulla strada tramite deinit()odefer . Questo perché la maggior parte del codice non sa cosa fare degli errori; sa cosa è andato storto, ma non ha abbastanza informazioni su ciò che un codice di livello superiore sta cercando di fare per sapere cosa fare dell'errore. Non sa se presentare una finestra di dialogo all'utente è appropriato, se deve riprovare o se qualcos'altro è appropriato.

Il codice di livello superiore, tuttavia, dovrebbe sapere esattamente cosa fare in caso di errore. Pertanto, le eccezioni consentono a errori specifici di risalire da dove si verificano inizialmente a dove possono essere gestiti.

La gestione delle eccezioni viene effettuata tramite catchistruzioni.

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

Puoi avere più dichiarazioni catch, ognuna con un diverso tipo di eccezione.

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

Per maggiori dettagli sulle migliori pratiche con eccezioni, consultare http://exceptionsafecode.com/ . Si rivolge specificamente al C ++, ma dopo aver esaminato il modello di eccezione Swift, credo che le basi si applichino anche a Swift.

Per dettagli sulla sintassi di Swift e sul modello di gestione degli errori, consultare il libro The Swift Programming Language (Swift 2 Prerelease) .


Fondamentalmente catturare se stesso in grado di gestire l'errore? o funzione di input
Farhad

1
@BrianS Non sono sicuro di quello che mi stai chiedendo, soprattutto per quanto riguarda una "funzione di input", ma "catch" è essenzialmente un sinonimo di "handle" nel contesto delle eccezioni. Vale a dire, catturare un'eccezione e gestirne un'eccezione sono la stessa cosa, per quanto riguarda i linguaggi di programmazione.
bames53

Ho un errore che tace, non capisco, mi può aiutare per favore? Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
Farhad,

@BrianS Sembra che tu stia utilizzando una funzione con una firma errata da qualche parte. Qualcosa si aspetta che venga assegnata una funzione che accetta NSData?, NSURLResponse?, NSError?come argomenti, ma gli stai dando una funzione che non accetta alcun argomento.
bames53

O qualcosa si aspetta che una funzione non dichiarata generi eccezioni e tu le dia una funzione che genera eccezioni.
bames53
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.