В предыдущей статье мы говорили о дженериках.



Было сложно повторить эту информацию, но именно поэтому мы тренируемся.

Тот факт, что я пишу эту серию для обучения, не означает, что этого достаточно для вас, чтобы понять все, чему учат. Возможно, вы перешли от одной из моих статей к другой по теме, и я не буду обвинять вас в этом. На самом деле, я призываю вас продолжать учиться из разных источников.

Я ценю преданность, которую вижу в людях, которые читали и / или хлопали по каждой из моих статей, но моя главная цель - не завоевать популярность. Это делается для того, чтобы, прочитав одну из моих статей, вы узнали что-то новое.

Больше нечего сказать, поехали.

Обработка ошибок

Вы когда-нибудь заходили на кухню готовить еду и понимали, что чистой посуды не бывает?

У вас есть несколько вариантов:
А. Отказаться от ужина
Б. Сходить в ресторан или
В. Помойте посуду, а затем приготовьте ужин дома.

Эти решения влияют не только на вас, но и на людей, которых вы кормите.

Вариант А - самое простое решение, но все голодают и смотрят на вас, как на спасителя обеда.

Вариант Б тоже прост, но он дороже, требует небольшой дополнительной работы, которую вам не пришлось бы делать, и не всегда имеет такой же вкус, как домашняя еда.

Вариант C - трудный путь, но это лучшее решение, если вы хотите, чтобы ваша еда была на вкус так, как вы хотите, с небольшим количеством жалоб и, надеюсь, без разбитой посуды.

Итак, какое отношение все это имеет к обработке ошибок? Все!

Когда вы решили попробовать приготовить ужин, вы назвали метод.

Мы действительно не слишком много думаем об обработке ошибок. Если бы это было наше приложение, возможно, половина наших пользователей действительно получала бы еду, а остальные сломали бы тарелку и ничего не получили.

Мы делаем это настолько естественно каждый день, что когда мы начинаем программировать, некоторые разработчики просто дают сбой своих приложений и говорят: «О, это вина пользователя».

[Звук зуммера] Неверный ответ! Если приложение дает сбой, когда его использует кто-то другой, это ВАША вина!

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

Я хочу, чтобы вы задумались на минуту, на высоком уровне, о тех местах, где что-то может пойти не так, как указано в fixDinner() выше. Действительно.

Так что я надеюсь, что вы действительно это сделали, вместо того чтобы продолжать читать. Но ответ таков: пять основных вещей, которые могут пойти не так в приведенном выше коде, и в каждом из них может быть несколько ошибок внутри него. Без дополнительной информации ответ неоднозначен. Но я также сказал высокий уровень, так что это означает пять вещей.

  1. Вы не получаете никаких блюд.
  2. Вы не получаете кастрюль.
  3. У тебя нет еды.
  4. Вы сжигаете обед.
  5. Вы роняете тарелку во время подачи, и картинка выше оказывается на полу.

Вот тут-то и появляется обработка ошибок.

Мы делаем это так, чтобы просто назначать все как необязательные, а затем обрабатывать их с помощью операторов _2 _, _ 3_ или guard.

Но что произойдет, если вы захотите получить окончательный ответ сейчас? Есть что-то, что называется do/catch заявлением.

Формат оператора do/catch - это do некоторый бит кода; если что-то может выдать ошибку, мы будем try, чтобы получить значение, которое он возвращает, или выполнить действие, которое оно выполняет. Если метод завершился успешно, мы выходим из оператора do/catch. Если это не удается, мы catch ошибку и даем ей имя, чтобы мы могли использовать ее в catch теле, используя let error. Как мы это называем, на самом деле не имеет значения, вы можете изменить error на все, что захотите, просто знайте, что это тип Error. Свойство Error localizedDescription - это просто вычисляемое свойство, которое возвращает строковое описание того, что пошло не так. Вы можете опустить
let error, если хотите, и Swift автоматически предоставит вам error константу, которую вы можете использовать в теле catch.

Я хочу поговорить о try на секунду. Вы можете использовать три разных try параметра.

try - попытаться выполнить эту функцию и вернуть результат или подготовиться к отлову ошибки в случае неудачи

try? - попробуйте выполнить эту функцию, в случае успеха верните необязательное значение, иначе не беспокойтесь о возникших ошибках.

try! - попробуйте выполнить эту функцию, я знаю, что она не вернет ошибку, и если это произойдет, МОЖНО ВЫБРАТЬ ПРИЛОЖЕНИЕ, потому что я не обрабатываю ошибку.

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

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

Когда вы используете try? или try!, вам не нужно заключать код в выражение do/catch.

Так как же нам выдавать ошибки?

Ошибки могут быть вызваны добавлением throws после параметров в объявлении функции.

Если вы хотите создавать свои собственные ошибки, вы можете! Вам просто нужно создать перечисление, соответствующее протоколу Error.

Когда мы используем throw, мы можем использовать MyAppSpecificErrors.openFileError, чтобы обозначить, что файл не может быть открыт. Однако для этого нет localizedDescription, поэтому мы не можем сообщить пользователю о том, что что-то пошло не так, не будучи очень техническими. Если бы мы использовали его вот так, у нас был бы do/catch, который выглядел бы примерно так.

В приведенном выше примере у нас есть отдельные catch утверждения для каждого потенциального сбоя.

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

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

На самом деле вам нужно написать только операторы catch для тех ошибок, которые могут возникнуть, но у вас должен быть последний оператор catch, который предназначен для всего неспецифического.

Но что, если мы хотим вернуть строку ошибки из функции бросания? У нас есть вариант, о котором я намекнул ранее. localizedDescription предназначен для всех ошибок Apple, но он не просто волшебным образом читает текст fileOpenError и выдает текст «Невозможно открыть файл». Мы должны сказать ему, что предоставить.

Здесь у нас есть перечисление, которое охватывает только FileErrors, которое принимает String и Error. Мы должны принять String, чтобы предоставить строковые значения для наших ошибок.

Затем у нас есть два случая, каждому из которых назначен собственный текст, или rawValue. Это позволяет нам создать вычисляемое свойство с подходящим названием, в данном случае я назвал его localizedDescription и присвоил ему тип String.

Когда кто-то звонит FileErrors.kOpenFailure.localizedDescription, он получает необработанное значение kOpenFailure, равное "Unable to open file".

Вы не хотите throw localizedDescription, только ошибку, и когда вы позже вызовете localizedDescription для ошибки, вы все равно получите необработанное значение. Необработанное значение может быть значением String, Character, Integer или FloatingType.

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

В следующем уроке мы поговорим о Enums подробнее, но пока мы просто добавляем то, что уже знаем.

Еще одна вещь, которую вам следует знать о бросании функций, - это ключевое слово rethrows.

Помните, когда мы говорили о замыканиях и определяли их как функцию, которая принимает другую функцию в качестве параметра? Если функция, переданная в качестве параметра, является функцией выброса, мы используем ключевое слово rethrows вместо throws, чтобы обозначить, что эта функция вызовет ошибку, которая была вызвана ее функцией-параметром. Вот пример:

throwingFunction принимает Int и помечен throw, это соответствует сигнатуре параметра, который hopeThisWorks использует для своего параметра.

Мы вызываем hopeThisWorks в операторе do/catch, используя try. Мы передаем throwingFunction(5) в качестве параметра. Но что, если throwingFunction действительно должен был принимать значение 3. Это вызовет ошибку, которая будет обнаружена в hopeThisWorks, а затем hopeThisWorks вернет rethrow ошибку туда, где она была вызвана.

Вначале я сказал вам, что у вас есть три варианта. В переводе на ваше приложение давайте вернемся к этим трем параметрам.

A. Подождите, пока приложение выйдет из строя
Б. Используйте стороннюю библиотеку, которая может добавить больше функций обработки ошибок, чем вам нужно.
C. Напишите код, необходимый для обработки ваших ошибок.

До этой статьи вы могли выбрать А или Б. Но я здесь, чтобы помочь вам научиться ловить рыбу. Теперь вы знаете достаточно, чтобы использовать C.

Резюме

Вполне нормально написать свою программу без обработки ошибок. Просто отметьте каждое место, где вам может понадобиться обработать ошибку, с помощью комментария
//TODO: Handle Error. Таким образом, вы можете вернуться к своему коду, когда закончите обрабатывать ошибки в неидеальной среде. Если при тестировании приложения вы столкнетесь с ошибкой, попробуйте обработать ее.

Итак, мы узнали о том, как отлавливать ошибки, как генерировать ошибки, как создавать ошибки и устанавливать описания для случаев ошибок и как повторно генерировать ошибки. Не так уж плохо.

Что дальше

Следующее - Enums с Generics и Closures.

Как раз когда вы думали, что мы закончили с замыканиями и обобщениями, я говорю вам, что в следующей статье они все вместе соберутся. Не волнуйтесь, эта следующая часть будет довольно простой по сравнению с изучением ее в первый раз.

Мы поговорим о некоторых более продвинутых способах использования Enums, например о закрытии кейсов. Дженерики тоже играют в этом роль, но я представлю их вам в простой для освоения и удобной форме.

А пока попробуйте вернуться к написанному вами коду и посмотреть, есть ли место для обработки ошибок. Если нет, я предлагаю вам написать программу командной строки, которая считывает и записывает данные в файл. Я не учил вас ни одному из них, но это будет еще один из многих уроков по поиску того, что вы ищете, в Stack Overflow или Google. Чтобы получить подсказку, что искать, попробуйте это.

Как всегда, продолжайте практиковаться! Мы почти закончили!