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

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

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


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.