Podczas wywoływania funkcji, która jest zadeklarowana throws
w języku Swift, należy oznaczyć witrynę wywołania funkcji za pomocą try
lub 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, foo
automatycznie 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ą defer
kodu 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 catch
instrukcji.
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) .