Мы используем обработку ошибок с помощью do-try-catch для реагирования на исправимые ошибки. Это дает нам больший контроль над различными ошибочными сценариями, которые могут возникнуть в нашем коде, например, при вводе неправильного имени пользователя или пароля.
Зачем нужно отслеживать ошибки?
В приложении некоторые ошибки могут быть результатом ошибок программиста. Когда ваше приложение вылетает с сообщением «Index out of bounds», вы, вероятно, где-то допустили ошибку. И точно так же, когда вы принудительно извлекаете опциональное значение, которое имеет значение nil, ваше приложение падает.
В практической разработке iOS некоторые ошибки являются частью работы приложения, например, сообщение «Недостаточно средств» при попытке оплаты с помощью карты.
Ошибки такого рода исправимы. Их можно обработать и отреагировать соответствующим образом. К примеру:
- При попытке снять деньги в банкомате отображается «Неверный PIN-код».
- Ваш автомобиль показывает индикатор низкого уровня топлива при попытке запустить двигатель.
- Попытка аутентификации для API возвращает «Неверное имя пользователя / пароль».
Вы можете работать с этими ошибками, отобразив сообщение с предупреждением или сделав что-то еще. Например, ваша карта блокируется после 3 неудачных попыток. Ваш автомобиль может указать вам ближайшую заправку, когда у вас кончается бензин. Вы можете попробовать ввести другое имя пользователя и пароль.
Swift поддерживает обработку ошибок с помощью блока кода do-try-catch.
Выбрасывание ошибок
Когда в вашем коде возникает сценарий, который может привести к ошибке, вы можете выбросить ошибку:
if fuel < 1000 { throw RocketError.insufficientFuel }
В приведенном выше коде ключевое слово throw используется для выдачи ошибки типа RocketError.insufficientFuel, когда переменная fuel меньше, чем 1000.
Представьте, что мы пытаемся запустить ракету:
func igniteRockets(fuel: Int, astronauts: Int) throws { if fuel < 1000 { throw RocketError.insufficientFuel } else if astronauts < 3 { throw RocketError.insufficientAstronauts(needed: 3) } // Запуск ракеты print("3... 2... 1... Старт!") }
Функция igniteRockets(fuel:astronauts:) будет запускать ракеты, только если значение fuel больше или равно 1000 и если на борту есть как минимум 3 астронавта.
Функция igniteRockets(…) также помечается ключевым словом throws. Это ключевое слово указывает, что ошибки должны быть обработаны.
В приведенном выше коде мы используем тип ошибки RocketError:
enum RocketError: Error { case insufficientFuel case insufficientAstronauts(needed: Int) case unknownError }
Данное перечисление определяет три типа ошибок: .insufficientFuel, insufficientAstronauts(needed) и .unknownError. Определение ваших собственных типов ошибок полезно, потому что это поможет четко понять, что эти ошибки означают в вашем коде.
Возьмем, например, ошибку insufficientAstronauts(needed:). Когда выдается эта ошибка, вы можете использовать аргумент needed:, который указывает, сколько астронавтов необходимо для успешного запуска ракеты.
Ключевое слово throw имеет тот же эффект, что и ключевое слово return. Когда выполняется throw, выполнение функции останавливается, и выброшенная ошибка передается вызывающей функции.
Обработка ошибок с помощью Do-Try-Catch
Теперь мы можем использовать обработку ошибок, чтобы соответствующим образом реагировать на сценарии ошибок. Обработка ошибок в Swift осуществляется с помощью блока кода do-try-catch:
do { try igniteRockets(fuel: 5000, astronauts: 1) } catch { print(error) }
Обработка ошибок имеет три аспекта:
- К функции, которая может вызвать ошибку, добавляется ключевое слово try.
- Блок кода, который включает ключевое слово try, обернут в do { … }.
- Один или несколько блоков catch { … } могут быть присоединены к do { … } для обработки отдельных случаев ошибок.
Если функция выдает ошибку, вызывается блок catch. В данном случае это выводит ошибку на консоль.
Вы также можете реагировать на ошибки в индивидуальном порядке:
do { try igniteRockets(fuel: 5000, astronauts: 1) } catch RocketError.insufficientFuel { print("Ракете нужно больше топлива!") } catch RocketError.insufficientAstronauts(let needed) { print("Нужно как минимум \(needed) астронавта...") }
Вышеуказанные блоки catch вызываются на основании отдельных перечислений RocketError. Вы можете напрямую получить доступ к связанным значениям перечислений, таких как needed. Также вы можете использовать выражения с шаблоном where чтобы получить больший контроль над ошибочным сценарием.
enum RocketError: Error { case insufficientFuel case insufficientAstronauts(needed: Int) case unknownError } func igniteRockets(fuel: Int, astronauts: Int) throws { if fuel < 1000 { throw RocketError.insufficientFuel } else if astronauts < 3 { throw RocketError.insufficientAstronauts(needed: 3) } // Ignite rockets print("3... 2... 1... Полет!") } do { try igniteRockets(fuel: 5000, astronauts: 1) } catch { print(error) }
Преобразование ошибок в опционалы с помощью try?
Целью обработки ошибок является явное определение того, что происходит при возникновении ошибки. Это позволяет нам реагировать определенным образом на ошибки.
В некоторых случаях нас не волнует сама ошибка. Нам просто нужно получить значение из функции. И если возникает ошибка, мы можем возвратить nil. Эта возможность доступна с помощью ключевого слова try? Когда вы используете try?, вам не нужно использовать полный блок кода do-try-catch.
let result = try? calculateValue(for: 42)
Представьте, что функция может выбрасывать ошибки, если ее параметр недействителен. Вместо обработки ошибки мы конвертируем возвращаемое значение в опциональное.
Возможны два сценария:
- Функция не выдает ошибку и возвращает значение, которое присваивается константе result.
- Функция выдает ошибку и не возвращает значение. Это означает, что константе result присваивается nil.
Обработка ошибок с помощью try? означает, что вы можете воспользоваться синтаксисом, специфичным для опционалов, таких как объединение по nil и опциональное связывание:
if let result = try? calculateValue(for: 99) { // } let result = try? calculateValue(for: 123) ?? 101
Отключение обработки ошибок с помощью try!
Вы можете полностью отключить обработку ошибок с помощью try!. Ключевое слово try! используется для принудиального извлечения опционального значения.
В отличие от того try?, который возвращает опциональное значение, синтаксис try! приведет к сбою вашего кода в случае возникновения ошибки. Есть два различных сценария, в которых использование try! может быть полезно:
- Используйте try!, когда вы на 100% уверенны, что ошибка не возникнет.
- Используйте, try!, когда невозможно продолжить выполнение кода.
Представьте, что вы пишите приложение, в которое встроен файл базы данных. Функция загрузки базы данных выдает ошибку, когда база данных повреждена. Вы можете использовать try! для загрузки базы данных в память, потому что, если база данных повреждена, приложение все равно не сможет быть использовано.
Спасибо Автору
Блог бога