Swift 2: Call może rzucić, ale nie jest oznaczony jako „try”, a błąd nie jest obsługiwany

161

Po zainstalowaniu Xcode 7 beta i przekonwertowaniu mojego kodu Swift do Swift 2, mam problem z kodem, którego nie mogę rozgryźć. Wiem, że Swift 2 jest nowy, więc szukam i sprawdzam, ponieważ nic na ten temat nie ma, powinienem napisać pytanie.

Oto błąd:

Call może rzucić, ale nie jest oznaczony „try”, a błąd nie jest obsługiwany

Kod:

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

    }

Migawka: wprowadź opis obrazu tutaj

Farhad
źródło

Odpowiedzi:

168

Musisz złapać błąd, tak jak już robisz dla swojego save()połączenia, a ponieważ obsługujesz tutaj wiele błędów, możesz trywiele wywołań sekwencyjnie w jednym bloku do-catch, na przykład:

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

Lub, jak @ bames53 wskazał w komentarzach poniżej, często lepiej jest nie wychwytywać błędu w miejscu, w którym został zgłoszony. Możesz oznaczyć metodę jako throwswtedy, tryaby wywołać metodę. Na przykład:

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
źródło
To pomoże mi to rozgryźć, dziękuję.
Farhad
5
Właściwie nie jest wymagane, aby wyjątek został tutaj złapany. Można po prostu dodać trysłowo kluczowe do wywołania funkcji i zadeklarować tę funkcję jako func deleteAccountDetail() throw. Lub jeśli masz zagwarantowane, że funkcja nie będzie rzucać dla danego wejścia, możesz użyć try!.
bames53
4
Nie podchodzę do tego po to, aby szukać dziurawych zębów, ale ponieważ w rzeczywistości dość ważna jest przyzwoita obsługa błędów oparta na wyjątkach, aby większość miejsc, w których występują wyjątki, nie przechwytuje wyjątków. Są trzy rodzaje miejsc, w których wyłapywanie wyjątków jest właściwe. We wszystkich innych miejscach kod nie powinien jawnie obsługiwać wyjątków i powinien polegać na niejawnych deinit()wywołaniach w celu wyczyszczenia (tj. RAII) lub czasami używać defergo do czyszczenia ad hoc. Aby uzyskać więcej informacji, zobacz exceptionsafecode.com (mówi o C ++, ale podstawowe zasady dotyczą również wyjątków Swift).
bames53
Ale jak uruchomisz tę funkcję? Jeśli pójdę z @ bames53 sposób?
Farhad
1
@NickMoore To, co programiści Swift zdecydowali na ich nazwanie, nie ma znaczenia, czym w rzeczywistości są. Nowy system obsługi błędów w Swift to implementacja wyjątków, ponieważ termin ten jest powszechnie używany w całej branży.
bames53
41

Podczas wywoływania funkcji, która jest zadeklarowana throwsw języku Swift, należy oznaczyć witrynę wywołania funkcji za pomocą trylub try!. Na przykład, biorąc pod uwagę funkcję rzucającą:

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

tę funkcję można nazwać następująco:

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

Tutaj adnotujemy wywołanie try, które woła do czytelnika, że ​​ta funkcja może zgłosić wyjątek, a kolejne wiersze kodu mogą nie zostać wykonane. Musimy również oznaczyć tę funkcję adnotacją throws, ponieważ ta funkcja może zgłosić wyjątek (tj. Kiedy willOnlyThrowIfTrue()wyrzuca, fooautomatycznie wyrzuci wyjątek w górę.

Jeśli chcesz wywołać funkcję, która jest zadeklarowana jako potencjalnie rzucająca, ale o której wiesz, że nie będzie rzucać w twoim przypadku, ponieważ podajesz jej poprawne dane wejściowe, możesz użyć try!.

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

W ten sposób, jeśli gwarantujesz, że kod nie zostanie zgłoszony, nie musisz umieszczać dodatkowego standardowego kodu, aby wyłączyć propagację wyjątków.

try!jest wymuszane w czasie wykonywania: jeśli użyjesz, try!a funkcja zakończy się rzucaniem, wykonanie programu zostanie zakończone błędem w czasie wykonywania.

Większość kodu obsługi wyjątków powinna wyglądać tak, jak powyżej: albo po prostu propagujesz wyjątki w górę, gdy się pojawią, albo ustawiasz warunki w taki sposób, że w przeciwnym razie możliwe wyjątki są wykluczone. Każde czyszczenie innych zasobów w kodzie powinno nastąpić poprzez zniszczenie obiektu (tj. deinit()) Lub czasami za pomocą deferkodu 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.
}

Jeśli z jakiegoś powodu wyczyścisz kod, który musi działać, ale nie jest w deinit()funkcji, możesz użyć 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)
}

Większość kodu, który zajmuje się wyjątkami, po prostu propaguje je w górę do wywołań, wykonując po drodze porządki za pośrednictwem deinit()lub defer. Dzieje się tak, ponieważ większość kodu nie wie, co zrobić z błędami; wie, co poszło nie tak, ale nie ma wystarczających informacji o tym, co jakiś kod wyższego poziomu próbuje zrobić, aby wiedzieć, co zrobić z błędem. Nie wie, czy przedstawienie użytkownikowi okna dialogowego jest właściwe, czy powinno spróbować ponownie, czy też coś innego jest właściwe.

Kod wyższego poziomu powinien jednak dokładnie wiedzieć, co zrobić w przypadku jakiegokolwiek błędu. Dlatego wyjątki pozwalają na pojawienie się określonych błędów z miejsca, w którym wystąpiły, do miejsca, w którym można je obsłużyć.

Obsługa wyjątków odbywa się za pośrednictwem catchinstrukcji.

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

Możesz mieć wiele instrukcji catch, z których każda przechwytuje inny rodzaj wyjątku.

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

Więcej informacji na temat najlepszych praktyk z wyjątkami można znaleźć pod adresem http://exceptionsafecode.com/ . Jest przeznaczony specjalnie dla C ++, ale po zbadaniu modelu wyjątków Swift, uważam, że podstawy dotyczą również języka Swift.

Szczegółowe informacje na temat składni języka Swift i modelu obsługi błędów można znaleźć w książce The Swift Programming Language (Swift 2 Prerelease) .

bames53
źródło
Zasadniczo złapać sam może sobie poradzić z błędem? lub funkcja wejścia
Farhad
1
@BrianS Nie jestem pewien, o co dokładnie pytasz, szczególnie w odniesieniu do „funkcji wejściowej”, ale „catch” jest zasadniczo synonimem „handle” w kontekście wyjątków. Oznacza to, że przechwytywanie wyjątku i obsługa wyjątku to to samo, jeśli chodzi o języki programowania.
bames53
Mam jeden błąd, którego cicho nie rozumiem, czy możesz mi pomóc? Invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'
Farhad
@BrianS Wygląda na to, że używasz gdzieś funkcji z nieprawidłowym podpisem. Coś oczekuje, że otrzyma funkcję, która przyjmuje NSData?, NSURLResponse?, NSError?jako argumenty, ale dajesz mu funkcję, która nie przyjmuje żadnych argumentów.
bames53
Albo coś oczekuje, że funkcja, która nie została zadeklarowana, będzie rzucać wyjątki, a ty nadajesz jej funkcję, która rzuca wyjątki.
bames53