Пытаться? Пытаться! Попробуйте: справиться с неожиданными результатами

Последнее обновление 15 мая 2017 г. | Swift 3.1

Моя история

Когда я был моложе, я открыл Swift Documentation. Я прочитал каждую главу более одного раза, кроме Error Handling. По какой-то причине эта фраза произвела у меня впечатление, будто я должен быть профессиональным отладчиком, чтобы полностью понять эту главу.

Даже тогда я боялся Error Handling. Такие слова, как catch, try, throw и throws, не имели для меня особого смысла. Они выглядели страшно. Если вы никогда не встречали их раньше, не кажутся ли они вам ужасными? Не волнуйся, мой друг. Я здесь ради тебя.

Я сказал своей 13-летней сестре, что обработка ошибок - это еще один способ написать if-else блок для отправки сообщения об ошибке.

Сообщение об ошибке от Tesla

Как вы знаете, автомобили Tesla имеют функцию автопилота. Но, когда автомобиль по каким-либо причинам сбивается с толку, он просит вас держаться за руль и показывает сообщение об ошибке. В этом уроке вы узнаете, как передать это сообщение с помощью Error Handling

Мы создадим программу, которая обнаруживает такие объекты, как светофоры на улице. Предпосылками являются, помимо прочего, машинное обучение, векторное исчисление, линейная алгебра, вероятность и дискретная математика. Шучу.

Представляем оператор If-Else

Чтобы в полной мере оценить обработку ошибок в Swift, нам следует заглянуть в ваше прошлое. Итак, это то, что многие, если не все, новые разработчики сделали бы, чтобы иметь дело с сообщением об ошибке,

var isInControl = true
func selfDrive() {
 if isInControl {
  print("You good, let me ride this car for ya")
 } else {
  print("Hold the handlebar RIGHT NOW, or you gone die")
 }
}
selfDrive() // "You good..."

Проблема

Самая большая проблема - это его читабельность, когда блок else становится раздутым. Во-первых, он не указывает, содержит ли сама функция сообщение об ошибке, если вы не прочитаете всю функцию полностью, если вы не назовете функцию, например, selfDriveCanCauseError, которая по-прежнему работает.

Смотрите, функция может убить драйвер. Вы должны четко проинформировать членов вашей команды о том, что эта функция опасна и даже фатальна, если с ней не обращаться осторожно.

Вторая проблема заключается в том, что если вы выполните какие-то сложные функции или действия в блоке else? Например,

else {
 print("Hold the handle bar Right now...")
 // If handle not held within 5 seconds, car will shut down 
 // Slow down the car
 // More code ...
 // More code ...
}

Блок else становится толстым, и это похоже на то, что вы пытаетесь бросить баскетбольный мяч в зимней одежде, что я часто делаю, поскольку в Корее довольно холодно. Вы знаете, что я говорю? Это просто не выглядит красиво и не читается.

Что ж, вы можете добавлять функции в блок else вместо жесткого кодирования.

else { 
 slowDownTheCar()
 shutDownTheEngine()
}

Однако, наряду с первой проблемой, нет явного способа указать, что эта selfDrive() функция опасна, и с ней следует обращаться с осторожностью. Итак, давайте погрузимся в Error Handling, чтобы писать модульные и явные сообщения об ошибках.

Введение в обработку ошибок

Пока вы знаете о проблеме с If-else для обработки сообщений об ошибках. Пример выше был слишком простым. Вместо этого предположим, что есть два сообщения об ошибке: 1. вы заблудились 2. разрядился аккумулятор автомобиля.

Я собираюсь создать enum, который соответствует Error протоколу.

enum TeslaError: Error {
 case lostGPS
 case lowBattery 
}

Честно говоря, я действительно не знаю, что делает Error протокол, но для Error Handling вам просто нужно делать это. Например, Почему ваш ноутбук включается, когда вы нажимаете кнопку? Почему ваш телефон разблокируется, когда вы проводите пальцем по экрану?

Именно так решили реализовать Swift инженеры, поэтому я не собираюсь подвергать сомнению их мотивы. Я слежу за тем, что они для нас сделали. Конечно, если вы хотите учиться, вы можете загрузить исходный код Swift и проанализировать его так же, как вы открываете свой ноутбук или iPhone. Я просто не буду этого делать.

Если вы заблудились, принесите мне еще несколько абзацев. Вы увидите, как TeslaError все объединится в функцию.

Давайте сначала отправим сообщения об ошибках, без Error Handling.

var lostGPS: Bool = true
var lowBattery: Bool = false
func autoDriveTesla() {
 if lostGPS {
  print("I'm lost, bruh. Hold me tight")
  // A lot more code
 }
 if lowBattery {
  print("HURRY! 💀")
  // Loads of code 
 }
}

Итак, если бы я запустил это

autoDriveTesla() // I'm lost, bruh. Hold me tight

Но давайте реализуем Error Handling. Во-первых, вы должны указать функции явно, что она опасна и может отправлять / выдавать ошибки. Мы вставляем ключевое слово throws рядом с функцией.

func autoDriveTesla() throws { ... }

Теперь функция автоматически сообщает вашему товарищу по команде, что autoDriveTesla - другое животное, не читая весь функциональный блок.

Звучит отлично? Хорошо, пора отправлять / throw эти ошибки, когда драйвер обнаруживает lostGPA или lowBattery в блоке Else-If. Помните перечисление TeslaError?

func autoDriveTesla() throws {
 if lostGPS {
  throw TeslaError.lostGPS
}
 if lowBattery {
  throw TeslaError.lowBattery
}

Позвольте мне поймать вас

Если lostGPS равно true, функция отправит TeslaError.lostGPS. Но что вы будете делать после этого? Куда мы вставим это сообщение об ошибке и еще много кода для работы в блоке else предположительно?

print("Bruh, I'm lost. Hold me tight")

Хорошо, я не хочу вас ошеломлять, поэтому давайте начнем с того, как запустить эту функцию с прикрепленным ключевым словом throws.

Поскольку это другое животное, вы должны использовать функцию run, вызывая try внутри блока do. Вы спрашиваете: «Что?». Потерпи меня немного.

do {
 try autoDriveTesla() 
}

Я знаю, что у вас в голове: «Я действительно хочу распечатать это сообщение об ошибке, если не умрет мой драйвер».

Контент перенесен в личный блог. Если вы хотите прочитать остальную часть Обработки ошибок, посетите здесь.